Bonjour,
je m’intéresse à la création de format de fichier binaire ainsi que l'utilisation, auriez-vous des conseils, des ressources, des exemples, pour en créer un de toute pièce ?
ça me permettra aussi de progresser en C++
merci !
Bonjour,
je m’intéresse à la création de format de fichier binaire ainsi que l'utilisation, auriez-vous des conseils, des ressources, des exemples, pour en créer un de toute pièce ?
ça me permettra aussi de progresser en C++
merci !
Salut,
Un fichier, binaire ou pas, ça se manipule via un std::fstream, std::ofstream pour output et écrire dedans via write ou les opérateurs <<.
ifstream pour input et la lecture, les fonctions read et opérateurs >>.
Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
Un peu de programmation réseau ?
Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.
Mon conseil: Prends exemple sur le format PNG: Un format séparé en sections, chacune précédée d'un tag identifiant son type et de sa taille.
S'assurer que chaque section commence par sa taille, et que celle-ci ait elle-même un emplacement fixe et une taille fixe (32 bits minimum, 64 bits recommandé) ou au moins semi-fixe (en gros, il ne faut pas que tu aies besoin de "lire jusqu'à ce que ce ne soit plus un chiffre" comme dans l'horreur qu'est HTTP), t'épargnera bien des migraines.
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
merci pour vos conseils !!!
dans mes recherches, j'ai cherché une solution pour écrire un float dans un fichier de façon binaire, que pensez-vous de ça ? :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 union { float float_u4; char char_u4[4]; }; float_u4 = 1.234f; std::ofstream ofile; ofile.open ("test.bin", std::ofstream::binary); ofile.write (&char_u4[0], sizeof(char_u4)); ofile.close (); std::ifstream ifile; ifile.open ("test.bin", std::ofstream::binary); ifile.read (&char_u4[0], sizeof(char_u4)); ifile.close ();
Salut,
Pourquoi utiliser une unionD'autant plus que tu ne peux jamais être tout à fait sûr du nombre de bytes utilisés par un float...
utilises les méthodes de conversion "classiques" (reinterpret_cast dans le cas présent) sous une forme proche de
Tu pourrais même envisager de le faire de manière générique, sous une forme qui serait proche de
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 float read(std::ifstream & ifs){ float f; ifs.read(reinterpret_cast<char*>(&f), sizoef(f)); return f; } void write(std::ofstream & ofs, float const f){ ofs.write(reinterpret_cast<char>(&f), sizof(f)); }
qui pourra fonctionner avec tous type POD
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 /* pour aider le compilateur à déterminer le type de l'élément qu'il doit lire, nous pouvons envisager * de transmettre celui-ci en sous forme d'une référence non constante */ template <typename T> void read(std::ifstream & ifs, T & value){ ifs.read(reinterpret_cast<char*>(&value), sizeof(T)); } void write(std::ofstream & ofs, T const & value){ ofs.write(reinterpret_cast<char*>(&value), sizeof(T)); }
En cas de besoin, nous pourrons prévoir une spécialisation de cette fonction pour les types récurrents qui ne le sont pas (comme std::string, par exemple), mais ca, c'est une autre histoire
Quoi qu'il en soit, le code serait bien plus simple, non
Ah, et une dernière petite remarque: les fonction open et close n'ont normalement pas besoin d'être utilisées: le constructeur des flux (qu'ils soient d'entrée ou de sortie) est tout à fait en mesure d'ouvrir automatiquement un fichier, y compris en mode binaire, et leur destructeur le ferme automatiquement lorsque l'on quitte la portée dans lequel le flux est déclaré.
Une fonction main qui écrirait une donnée dans un flux binaire et qui le lirait tout de suite après pourrait donc prendre une forme proche de
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 int main(){ { std::ofstream ofs("fichier.bin",std::ios_base::binary); // ofs est automatiquement ouvert ici /* pour bien faire, il faudrait s'assurer que le fichier est bien ouvert, mais bon */ float w{3.1415926}; write(ofs, w); } // ofs est automatiquement fermé ici float r; { std::ifstream ifs("fichier.bin", std::ios_base::binary); // ifs est automatiquement ouvert ici /* pour bien faire, il faudrait s'assurer que le fichier est bien ouvert, mais bon */ read(ifs, r); } // ifs est automatiquement fermé ici std::cout<<"r: "<<r<<"\n"; return 0; }
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
naïvement, en quoi la référence non constante aide le compilateur a déterminer le type ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 /* pour aider le compilateur à déterminer le type de l'élément qu'il doit lire, nous pouvons envisager * de transmettre celui-ci en sous forme d'une référence non constante */ template <typename T> void read(std::ifstream & ifs, T & value){ ifs.read(reinterpret_cast<char*>(&value), sizeof(T)); } void write(std::ofstream & ofs, T const & value){ ofs.write(reinterpret_cast<char*>(&value), sizeof(T)); }
Alors le gros problème des float, c'est que le format d'un float n'est pas défini par le standard C++, et donc rien n'est garanti marcher partout!
Mais le "union-cast" devrait marcher sur la plupart des plate-formes qui utilisent des flottants IEEE 754.
Autre soucis: L'endianness doit être choisie et documentée, et le code de lecture de ton implémentation de référence doit marcher quelle que soit l'endianness de la machine pour laquelle il est compilé (que ce soit grâce à des #ifdef ou via un code indépendant de l'endianness de l'hôte). Pour ton format, que choisir?
- Le "standard" pour la communication, alias "Network Byte Order", est le big-endian. La norme POSIX (et l'API des sockets, respectée plus ou moins par Windows) contient des fonctions pour aider à convertir entre big-endian et "endianness de l'hôte", et rien de tout ça pour le little-endian.
- Mais toutes les plate-formes grand public actuelles (principalement x86 et Android) utilisent little-endian. Si ton code de lecture/écriture est basé sur des #ifdef selon l'architecture ciblée, ça rend le code de lecture trivial (et donc plus rapide) pour les plate-formes little-endian.
- Certains formats acceptent les deux, avec un champ indiquant quelle endianness ils utilisent.* L'intention est que le fichier soit systématiquement écrit dans l'endianness du code qui l'écrit, et lu la plupart du temps par la même endianness, avec un code "de secours" (pouvant être plus lent) utilisé quand un fichier provient d'une architecture d'endianness opposée.
De nos jours, ma recommandation est de choisir "little endian partout". Ma préférence pour le code est de faire un code indépendant de l'endianness de l'hôte, mais pour un débutant ça peut être compliqué, et avoir simplement un code pour architectures big-endian et un code trivial pour architectures little-endian peut être mieux.
Pour info, les BinaryReader et BinaryWriter du Framework .Net utilisent little-endian systématiquement.
*Ce qui nous amène à la question des formats auto-documentés, que j'aborde ci-après.
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
La question suivante: Veux-tu que ton format binaire puisse être auto-documenté?
C'est-à-dire, qu'il puisse comporter une section expliquant les types et tailles des différents champs qu'il comporte? (voire qu'il ait un indicateur de type avant chaque donnée, même si ça bouffe plus de place?
Cela permet de faire de la validation sur le contenu, d'être plus résistant à une mise à jour du format, etc. Mais c'est plus compliqué.
Et pour finir, la question la plus importante:
Veux-tu vraiment créer un format de toute pièces, et pourquoi?
Tu auras droit à une meilleure compatibilité si tu implémentes à la place un format "générique" existant.
Récemment, dans un de mes programmes en C#, j'ai implémenté du code pour lire le format de sérialisation binaire de .Net, ainsi que le format des PropertySet de Microsoft...
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Partager