IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Le problème de sauvegarde de donnée binaire en C++ et la portabilité


Sujet :

C++

  1. #1
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut Le problème de sauvegarde de donnée binaire en C++ et la portabilité
    Suite à un problème d'écriture de données binaires, j'ai poser cette question erreur-wtf-lecture-fichier-binaire/. La réponse était simple et propre au chaine de caractères et au caractère terminateur.

    Mais du coup ce pose le problème des données binaires en C++ : Est-il possible de contourner (facilement et simplement) le problème des Little/Big Endian et également celui de la taille réelle des variables car même avec l'utilisation du style uint64_t je constate que le compilateur coupe la taille réelle des données en mémoire. Exemple si ma variable contient un petit nombre, inférieur à 256, la variable ne fait qu'un seul octet en mémoire.

    Je sais qu'il existe des librairies de "serialization" mais je cherche une solution sans librairies tierces.


    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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    #include <fstream>
    #include <iostream>
     
    using namespace std;
     
    char* lire(const char* nomfichier)
    {
        int size;
        char * memblock = NULL;
     
        ifstream file (nomfichier, ios::in|ios::binary|ios::ate);
        if (file.is_open())
        {
        size = file.tellg();
     
    	//POUR TESTER LA TAILLE DE CE QUE J'ECRIS DANS LE FICHIER
    	cout << "size in read : " << size << endl;
     
    	memblock = new char [size];
     
        file.seekg (0, ios::beg);
     
    	//LECTURE DU FICHIER AVEC LA BONNE TAILLE VERIFIEE PLUS HAUT
    	file.read (memblock, size);
     
    	//ICI RIEN NE VA PLUS.... WTF
        cout << memblock << " : " << std::char_traits<char>::length(memblock) << '\n' << endl;
        file.close();
        }
        else cout << "Unable to open file";
     
        return memblock;
    }
     
    void ecrire(const char* nomfichier, const char* data)
    {
        int size;
        std::ofstream file (nomfichier, ios::out|ios::binary);
     
    	//TEST DE LA TAILLE EN ECRITURE -> OK
        size = std::char_traits<char>::length(data)+1;
        cout << "size in write : " << size << endl;
        file.write (data, size);
        file.close();
    }
     
    int main(int argc, char *argv[])
    {
        char* data;
        uint64_t* ui64;
        ui64 = new uint64_t(77);
        data = reinterpret_cast<char*>(ui64);
    	//TEST DE LA DONNEE ET DE SA TAILLE
        cout << data << " : " << std::char_traits<char>::length(data) << '\n' << endl;
     
        ecrire("test.txt", data);
     
        data = lire("test.txt");
     
        //LA REINTERPRETATION NE FONTIONNE PAS!!!
        *ui64 = reinterpret_cast<uint64_t>(data);
        cout << *ui64 << '\n' << endl;
        *ui64 = data[0];
        cout << *ui64 << '\n' << endl;
        cout << data << '\n' << endl;
     
     
        return 0;
    }

  2. #2
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Points : 1 878
    Points
    1 878
    Billets dans le blog
    21
    Par défaut
    Je n'ai pas l'opportunité de tester ton code, mais, avec tout le respect que je te dois, tu cumules les anti-patterns du C++: des cast à tout va, des new explicites, etc.
    Il me semble qu'il y a plutôt un problème de conception dans ton code.

    Il me semble que partir de fonctions template qui encapsulent tout ça rendrait la chose plus propre:

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename T>
    std::ostream& serialize(const T& obj, std::ostream& os) {
      os.write(reinterpret_cast<const char*>(&obj), sizeof(T));
      return os;
    }
     
    template <typename T>
    std::istream& unserialize(T& obj, std::istream& os) {
      os.read(reinterpret_cast<char*>(&obj), sizeof(T));
      return os;
    }

    Naturellement cela reste assez basique: tu ne pourras pas serialiser un objet complexe comme un vector uniquement de cette façon (il faut prendre en compte aussi la région mémoire vers laquelle le pointeur qu'il contient pointe)

  3. #3
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut
    Aucun problème avec ta remarque stendhal666, mon code n'est opti du tout. Les templates était prévu mais je constate que j'ai déjà pas mal de problème avec mon code, je voulais déjà tester la logique de façon simple... Je dois aussi admettre que je suis débutant en C++ et n'ai pas encore tout les bons réflexes.

    Merci pour ton code.

  4. #4
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Points : 1 878
    Points
    1 878
    Billets dans le blog
    21
    Par défaut
    Ne me remercie pas trop vite... il ne marche pas! (je l'ai modifié un peu d'ailleurs)
    J'essaie de comprendre...

  5. #5
    Expert éminent sénior

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 031
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 031
    Points : 11 379
    Points
    11 379
    Billets dans le blog
    10
    Par défaut
    Pour le problème de Little/Big endian, tu peux écrire un "Magic Number" en tête de ton fichier, qui te permettra de détecter l'endianness du fichier, et donc de t'adapter en fonction.

    Ensuite, pour la découpe des données, autant avec les opérateurs << et >> j'ai constaté le soucis, autant avec read et write je ne l'ai plus eu.

    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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #include <fstream>
    #include <iostream>
    #include <cstdint>
     
    template< typename T >
    std::ostream& serialize( T const & obj, std::ostream & os )
    {
      os.write( reinterpret_cast< char const * >( &obj ), sizeof( T ) );
      return os;
    }
     
    template< typename T >
    std::istream & unserialize( T & obj, std::istream & is )
    {
      is.read( reinterpret_cast< char * >( &obj ), sizeof( T ) );
      return is;
    }
     
    int main()
    {
      int64_t value;
      std::cin >> value;
      std::ofstream out( "tmp", std::ios::binary );
     
      if ( out )
      {
        std::cout << "Write value: " << value << std::endl;
        serialize( value, out );
        out.close();
      }
     
      std::ifstream in( "tmp", std::ios::binary );
      in.seekg( 0, std::ios::end );
      std::cout << "File size: " << in.tellg() << std::endl;
     
      if ( in )
      {
        unserialize( value, in );
        in.close();
        std::cout << "Read value: " << value << std::endl;
      }
    }
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

  6. #6
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut
    dragonjoker59, je suis d'accord que Little/Big Endian est un faux problème auquel il existe des solutions, "Magic Number" en est une bonne.

    Je viens de tester ton code et effectivement le problème à disparu mais nous sommes d'accord que m^me si mon code n'est pas terrible, il n'est pas faux!

    Je ne sais pas quoi penser de cette bizarrerie...

  7. #7
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut
    MDR , je viens d'en trouver une autre!

    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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    #include <fstream>
    #include <iostream>
    #include <cstdint>
     
    template< typename T >
    std::ostream& serialize( T const & obj, std::ostream & os )
    {
      os.write( reinterpret_cast< char const * >( &obj ), sizeof( T ) );
      return os;
    }
     
    template< typename T >
    std::istream & unserialize( T & obj, std::istream & is )
    {
      is.read( reinterpret_cast< char * >( &obj ), sizeof( T ) );
      return is;
    }
     
    struct Test_div_ui
    {
        int64_t value64;
        int32_t value32;
    };
     
    int main()
    {
      Test_div_ui tdui;
      //std::cin >> value;
      tdui.value32 = 77;
      tdui.value64 = 77;
      std::ofstream out( "tmp", std::ios::binary );
     
      if ( out )
      {
        std::cout << "Write value: " << tdui.value32 << ", " << tdui.value64 << std::endl;
        serialize( tdui, out );
        out.close();
      }
     
      std::ifstream in( "tmp", std::ios::binary );
      in.seekg( 0, std::ios::end );
      std::cout << "File size: " << in.tellg() << std::endl;
     
      if ( in )
      {
        unserialize( tdui, in );
        in.close();
        std::cout << "Read value: " << tdui.value32 << ", " << tdui.value64 << std::endl;
      }
    }

    La taille du fichier ne fait pas 12 mais 16 octets!

  8. #8
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    On appelle ça le padding, et c'est tout à fait normal pour le coup.

  9. #9
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut
    Iradrille, après quelles que recherche j'ai trouver l'histoire du padding. Je l'ignorerai merci pour cette précision, je me coucherai moins con. Pendant une minute, j'ai cru devenir fou... Merci

  10. #10
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut
    Du coup, nous arrivons au cœur du problème de la lecture/écriture de données binaires. Le padding en est un bon exemple...

    Quelles sont les solutions? écrire des octets de padding dans un fichier est d’après moi problématique, non?

  11. #11
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Points : 1 878
    Points
    1 878
    Billets dans le blog
    21
    Par défaut
    C'est tout à fait problématique.

    Le padding n'est pas standardisé, et différents compilateurs peuvent réagir différemment, différentes versions, différentes options de compilation ont le même effet.
    On peut pallier ce défaut en concevant soigneusement les structs à sérialiser pour que le padding ne soit pas nécessaire, mais cela reste un travail de déduction pas sûr à 100%
    Il me semble que les bibliothèques de sérialisation sérieuses, genre Boost, ont leur propre format de fichier, ce n'est pas du simple binaire.

  12. #12
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Points : 1 878
    Points
    1 878
    Billets dans le blog
    21
    Par défaut
    La façon de faire pour Boost est de surcharger un opérateur (<< ou &)
    Pour une struct:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct Padded {
      int64_t a;
      int32_t b;
      char c; // il y aura du padding par là à tous les coup
      int32_t d;
    };
    tu dois surcharger ton opérateur <<:

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Boost::archive& operator<<(Boost::archive& ar, const Padded& p) {
      ar << a << b << c << d; // du coup pas de padding
      return ar;
    }

  13. #13
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut
    Je n'ai pas encore fais le test mais je suppose que le problème du padding se présente également avec une Class, non?

  14. #14
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut
    stendhal666, si je comprends bien le code... (ce qui resta à voir), il n'y a pas de padding car il n'y a plus de structure mais uniquement ces composants?

  15. #15
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2013
    Messages
    610
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2013
    Messages : 610
    Points : 1 878
    Points
    1 878
    Billets dans le blog
    21
    Par défaut
    Oui et oui.

    En dehors de certaines struct (grosso modo celles qui auraient pu être déclarées en C, pour lesquelles il existe certaines garanties sur la disposition en mémoire), il n'y a que très peu d'indications dans le standard et donc il est impossible de spéculer efficacement sur la position relative, et encore moins la position absolue des membres. Il faut penser en plus à l'héritage, aux fonctions virtuelles, etc. qui prennent de la place également.

    Tandis qu'avec la formule boost on évite le problème en créant une instance simplifiée de la struct/class par la sérialisation de ses membres. Le problème c'est que si la classe est donnée et qu'elle ne donne pas accès à ses membres, on est un peu cuits.

  16. #16
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut
    Je ne veux pas, dans la mesure du possible, utiliser de librairies tierces telle que boost mais leurs solutions m’intéresse... Je me fais peut-être chier pour rien mais cela aura au moins une valeur pédagogique.

    Je vais réfléchir à tout ça au calme et coder un peut (prise en charge du little/big endian et "serializaion"). Je posterai le résultat plus tard.

    Merci de toutes vos réponses.

  17. #17
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut
    Je suis désolé mais j'ai encore du raté un chapitre.

    Pourrait-on m'expliquer pourquoi ce code:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
     
    int main()
    {
        unsigned char test[4] = { 0x00, 0x01, 0x01, 0x00 };
     
        std::cout << "Endian: " << std::hex << std::showbase << *(int32_t *) test << std::endl;
     
        return 0;
    }
    me donne un résultat avec 5 octets !!!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Endian: 0x10100
     
    Process returned 0 (0x0)   execution time : 0.013 s
    Press any key to continue.

  18. #18
    Expert éminent sénior

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 031
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 031
    Points : 11 379
    Points
    11 379
    Billets dans le blog
    10
    Par défaut
    Il n'y a pas 5 octets dans ton affichage, mais 2 1/2 (2 caractères pour 1 octet)
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

  19. #19
    Membre habitué
    Homme Profil pro
    sans activité
    Inscrit en
    Janvier 2016
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : sans activité

    Informations forums :
    Inscription : Janvier 2016
    Messages : 76
    Points : 136
    Points
    136
    Par défaut
    exact, je fatigue!

  20. #20
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    En ajoutant << std::setw(8), ça devrait mieux marcher.

    Par contre, je mets en question la sagesse d'utiliser une valeur palindrome ({ 0x00, 0x01, 0x01, 0x00 }) comme marqueur d'endianness...
    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.

Discussions similaires

  1. Réponses: 2
    Dernier message: 26/05/2011, 22h06
  2. [1.x] Problème de sauvegarde des données avec embed form
    Par Vicrabb dans le forum Symfony
    Réponses: 2
    Dernier message: 24/11/2010, 12h11
  3. [MySQL] problème de sauvegarde de données dans la base MySQL
    Par hassen07 dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 27/05/2010, 21h58
  4. Réponses: 2
    Dernier message: 19/09/2007, 21h00
  5. Problème pour sauvegarder mes données
    Par ploup dans le forum Windows Forms
    Réponses: 5
    Dernier message: 04/05/2007, 14h17

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo