Forum Solarus-Games francophone

Jeux amateurs => Programmation => Discussion démarrée par: compositeur le 05 Septembre 2012 à 11:08

Titre: [c++] écriture dans un fichier, démystification [résolu]
Posté par: compositeur le 05 Septembre 2012 à 11:08
Bonjour à tous,

  Je demande l'aide des programmeurs de ce forum via ce topic, effectivement, depuis quelques temps, je suis confronté à un problème en C++.

  Je m'amuse à coder un petit programme en c++ depuis quelques temps, qui nécessite de manipuler des fichiers. J'utilise la bibliothèque QT, donc pas de problème, il existe via cette bibliothèque des classes toutes faites pour manipuler facilement les fichiers. Mais voila : à un moment, dans mon programme, j'ai besoin d'écrire en plein milieu d'un fichier, sans écraser le reste. Après quelques recherches, je suis arrivé à la conclusion que cela était impossible, je pouvais écrire à la fin d'un fichier, à la place d'un fichier... mais pas en plein milieu.

  J'avais donc face à moi deux solutions :
  1) Copier le début du fichier dans un autre fichier temporaire. Placer à la suite les données dont j'avais besoin, puis y placer ensuite la fin du fichier d'origine. Ensuite, supprimer le fichier d'origine et renommer le fichier temporaire.
  2) Placer tout le fichier, ligne par ligne, dans un vector<string>, y apporter les modifications voulues, puis vider complètement le fichier en y écrivant à la place le contenu final de mon vector.

  Voici donc toutes mes questions :
  Pourquoi ne peut-on pas écrire au milieu d'un fichier ? A quoi sert la fonction ostream::seekp ?
  Laquelle, de mes deux méthodes, est la plus optimisée -en terme de temps d'execution- ?
  Existe-t-il des bibliothèques proposant des méthodes simples afin d'écrire au milieu d'un fichier ?
  Dans ma solution 2), j'ai décidé de stocker le contenu de mon fichier dans un vector<string>. Sachant que les fichiers que je manipule ont des tailles généralement comprises entre 50ko et 10Mo, est il possible de tout stocker dans un string ? Y a-t-il une limite de taille ?
Titre: Re : [c++] écriture dans un fichier, démystification
Posté par: Morwenn le 05 Septembre 2012 à 17:10
Alors, pour ta première question, lorsqu'on écrit dans un fichier déjà existant, il n'y a aucune copie de fichier de faite ni rien du tout, donc aucune perte mystérieuse de performance. Si on écrit "au milieu" d'un fichier, soit on écrase les données après la position où on a commencé à écrire, soit ça implique que les données soient d'abord copiées ailleurs et il y a donc une perte de performance. Et donc si on veut écrire dans un fichier sans perdre de données, on crée en général un fichier temporaire, donc on est dans le cas de ta première solution.

La fonction seekp sert à aller à une position donnée dans un fichier. Typiquement, la position est récupérée auparavant avec la fonction tellp. En gros, ça sert à revenir à un endroit du fichier marqué au préalable.

Pour savoir la quelle de tes deux méthodes est la plus optimisée, tu devras faire un benchmark à la main. Personnellement, je dirais la première : tu te contentes de lire un fichier et d'écrire dans un autre. Ta seconde méthode implique d'écrire dans des chaînes puis de réécrire dans un fichier, donc tu as deux fois plus d'opérations de faites.

Je ne sais pas s'il existe des bibliothèques prévues à cet effet. Tu peux chercher voir si une des nombreuses bibliothèques de Boost fournit une fonctionnalité similaire.

En général, les strings ont une limite de taille que tu peux récupérer via leur méthode string::max_size. Si tu essayes de mettre plus que ça dans une string, les fonctions qui le font risquent de retourner une exception.
Titre: Re : [c++] écriture dans un fichier, démystification
Posté par: compositeur le 05 Septembre 2012 à 18:22
Merci Morwenn ;)
Quelques petits détails me turlupinent cependant encore un peu.

Déja, qu'est-ce qu'un benchmark ?
Ensuite, j'ai lu que les opérations qui consistaient à lire/écrire dans des fichiers étaient des opérations très longues et coûteuses... Comment cela se peut il donc que la solution 1 soit plus rapide, au niveau du temps d'exécution, que la solution 2 ?
Et enfin, en lisant la documentation sur c++ reference de la fonction ostream::seekp, j'ai vu cet exemple :
#include <fstream>
using namespace std;

int main () {
 long pos;

 ofstream outfile;
 outfile.open ("test.txt");

 outfile.write ("This is an apple",16);
 pos=outfile.tellp();
 outfile.seekp (pos-7);
 outfile.write (" sam",4);

 outfile.close();

 return 0;
}


Visiblement, à la fin de l'exécution de ce code, le fichier est censé contenir "This is a sample". Donc, on a bien à faire ici à une écriture dans un fichier qui a eu lieu "au milieu", sans perte d'information  (et à ce propos, où est passé le 'n' de "an" ?) ?
Titre: Re : [c++] écriture dans un fichier, démystification
Posté par: Morwenn le 05 Septembre 2012 à 18:44
Un benchmark, c'est juste un test de vitesse en fonction de certains paramètres. Là en gros, je te disais juste de comparer toi-même la vitesse d'exécution ; c'est la seule méthode qui marche vraiment.

Dans l'exemple que tu donnes, il y a perte d'informations en fait : dans "this is an apple", "n ap" est remplacé par " sam" pour faire "this is a sample". Les données ont clairement été écrasées et non insérées.

Après, oui, les opérations d'entrées/sorties sont toujours coûteuses, mais là, dans tous les cas, tu devras réécrire dans un fichier à la fin, quoi que tu fasses. Sauf que dans la solutions avec les string, tu dois en plus d'abord tout réécrire dans des strings.

Après, si tu veux accélérer les opérations d'entrée/sortie, tu peux précéder ton code par
std::ios_base::sync_with_stdio(false);

En fait, en C++, par défaut, cette option est mise à true, c'est-à-dire que tous les flux d'entrée-sortie ont l'obligation de convertir les arguments d'une certaine manière et de faire appel aux fonctions C de cstdio. Si tu désactives la synchronisation, la compilateur se démerde et en général se débrouille pour faire ça de manière bien plus rapide. Cette simple ligne accélèrera de toutes façons ton code ;)
Titre: Re : [c++] écriture dans un fichier, démystification
Posté par: compositeur le 05 Septembre 2012 à 19:09
Et imaginons que je veuille écrire au milieu d'un document, et qu'écraser les données à l'endroit où j'écris m'importe peu, du moment que je garde le début et la fin, je peux utiliser seekp ? Si oui, j'imagine que cette opération est plus rapide que mes deux solutions précédemment envisagées ?
Titre: Re : [c++] écriture dans un fichier, démystification
Posté par: Morwenn le 05 Septembre 2012 à 19:31
Si l'option existait aussi simplement, je te l'aurais déjà donnée dès le premier post^^

Comme je te dis, la technique générale dans ces cas-là, c'est de recopier le fichier d'entrée dans un fichier temporaire jusqu'à l'endroit voulu, d'ajouter ce que tu veux dans le fichier temporaire, puis d'écrire la fin du fichier d'entrée par le fichier temporaire. Et puis après, de remplacer le fichier d'entrée par le fichier temporaire.
Titre: Re : [c++] écriture dans un fichier, démystification
Posté par: compositeur le 05 Septembre 2012 à 20:52
D'accord, mais dans ce cas qu'est-ce qui va se passer en utilisant la méthode donnée en exemple de seekp ? C'est viable sur des gros fichiers ? Peu importe sur ça m'écrase des données au milieu du fichier. Si j'ai bien compris, ça fonctionne un peu comme la touche "inser" quand on est dans un traitement de texte ? Si c'est le cas, alors ça serait pas mal, car techniquement, mon programme se fiche pas mal de ce qu'il y a dans le fichier, il faut juste qu'il garde grosso modo la même allure...
Titre: Re : [c++] écriture dans un fichier, démystification
Posté par: Morwenn le 05 Septembre 2012 à 22:07
Si tu n'as rien à faire d'écraser les données, alors en effet, le plus simple est d'écrire directement dedans, donc de l'ouvrir en mode read/write, de lire jusqu'aux endroits qui t'intéressent et d'écrire directement par-dessus.
Titre: Re : [c++] écriture dans un fichier, démystification
Posté par: compositeur le 06 Septembre 2012 à 00:25
De cette façon, je suis assuré que toute la fin du fichier ne sera pas effacée ?
Titre: Re : [c++] écriture dans un fichier, démystification
Posté par: Morwenn le 06 Septembre 2012 à 01:28
Je le dis et je le répète, tout ce que tu as à savoir, c'est qu'écrire au milieu d'un fichier n'insère aussi donnée, mais ça se contente d'écrire par-dessus les données déjà existantes. Maintenant, à toi de voir si ça t'emmerde ou non -______-
Titre: Re : [c++] écriture dans un fichier, démystification
Posté par: compositeur le 06 Septembre 2012 à 16:38
Ok...Merci...