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 :

Comportement différent GCC windows/linux : strlen ou getline?


Sujet :

C++

  1. #1
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut Comportement différent GCC windows/linux : strlen ou getline?
    Bonjour,

    Nous observons un comportement différent au niveau d'un programme écrit en C/C++ entre une compilation GCC sous windows (mingw / codeblock) et GCC linux (codeblock).

    En fait, pour lire dans un fichier une ligne,

    - nous ouvrons un flux sur le fichier à l'aide de :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::ifstream               fin; //déclaration
    fin.open(filename);              //ouverture du flux
    - nous déclarons un buffer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    char buf[MAX_LINE_LENGTH];
    - Nous lisons chaque ligne du fichier à l'aide de :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fin.getline(buf,MAX_LINE_LENGTH); //lecture
    - Pour pouvoir travailler avec les fonctions de la bibliothèque C++ <string>, nous convertissons ce buffer en std::string :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::string str;
    str = buf;
    - Ensuite, pour savoir si on est face à une ligne vide, nous utilisons :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if( strlen(str) == 0 ){
    //...
    }
    C 'est ici que tout se gâte! D'une part, sous linux, la fonction strlen n'est pas incluse dans string. Il faut donc rajouter cstring.

    Une fois rajoutée, le résultat est complètement différent de sous windows. Il semble qu'il y ait deux solutions possibles :
    - Soit "getline" supprime seule sous windows \r\n ou \n (std::endl d'un autre programme C++)
    - Soit "strlen" ne prend pas en compte les \r\n ou \n

    Quelle est selon vous la cause? Comment corriger ceci "proprement" ?

  2. #2
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Bonjour,

    Généralement, il vaut mieux éviter les mic-mac C/C++.
    La librairie standard C est conçu pour marcher uniquement avec du C.
    La librairie standard C++ est conçu pour bien marcher avec du C++, tout en s'efforçant de garder la compatibilité avec le C, ce qui n'est pas toujours sans heurt.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    std::ifstream               fin; //déclaration
    fin.open(filename);              //ouverture du flux
    char buf[MAX_LINE_LENGTH];
    fin.getline(buf,MAX_LINE_LENGTH); //lecture
    std::string str;
    str = buf;
    Lire un fichier avec des flux C++ pour remplir des tableaux de char C, puis reconvertir ces tableaux en string C++, c'est tout à fait possible, mais un peu tortueux

    Le code de la faq passe directement d'un flux C++ à des string C++.
    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
     
    #include <string>
    #include <fstream>
    #include <iostream>
     
    int main()
    {
        // le constructeur de ifstream permet d'ouvrir un fichier en lecture
        std::ifstream fichier( "fichier.txt" );
     
        if ( fichier ) // ce test échoue si le fichier n'est pas ouvert
        {
            std::string ligne; // variable contenant chaque ligne lue
     
            // cette boucle s'arrête dès qu'une erreur de lecture survient
            while ( std::getline( fichier, ligne ) )
            {
                // afficher la ligne à l'écran
                std::cout << ligne << std::endl;
            }
        }
    }
    Plus étonnant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    std::string str;
    ...
    if( strlen(str) == 0 )
    {
    //...
    }
    Je ne connais pas bien GCC, mais ça me semble hautement suspicieux que strlen, fonction de la librairie standard C, puisse accepter sans sourciller une std::string, classe de la librairie standard C++ !! .
    D'ailleurs avec Visual C++, ça ne compile pas.
    Citation Envoyé par bretus
    Comment corriger ceci "proprement" ?
    Pour obtenir la taille d'une std::string, il faut utiliser sa fonction membre size().

  3. #3
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Bonsoir,

    Humph, c'est vrai que j'ai fait l'âne et j'ai pas assez fouiné assez longtemps dans les docs et que j'ai pas trop réfléchi... J'ai vu un exemple sur cppreference.com que je trouve assez pratique pour rechercher les prototypes de fonctions (dans C++ IO pourtant ) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     ifstream fin("tmp.dat");
     
        int MAX_LENGTH = 100;
        char line[MAX_LENGTH];
     
        while( fin.getline(line, MAX_LENGTH) ) {
          cout << "read line: " << line << endl;
        }
    Je me suis dit pourtant que c'était idiot de pas pouvoir envoyer direct dans une std::string... J'avais oublié cette fonction : std::getline que je cherchais en temps que membre de la classe "input file stream"... A priori, std::getline me supprimera proprement mes caractères de fin de ligne...

    Pour le strlen, c'est sur que ça mérite une torche... Elle semble être définie dans <cstring> pour ce compilo... C'est moche, mais elle ne devait pas poser problème logiquement, elle doit renvoyer le size() de la std::string en paramètre...

    Bref, merci beaucoup pour ces précisions et les corrections qui en découleront. J'essayerai d'être plus frileux sur les mixtures en C++/C quand j'ai le choix à l'avenir...

    Bonne soirée

    ps : J'avoue quand même que des fonctions d'une même lib d'un "même" compilo ne fasse pas la même chose, ça me choque...

  4. #4
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 382
    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 382
    Points : 41 588
    Points
    41 588
    Par défaut
    Pourquoi ne pas utiliser directement une string?

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     ifstream fin("tmp.dat");
     
        string line;
     
        while( getline(fin, line) ) {
          cout << "read line: " << line << endl;
        }

  5. #5
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Bonjour, merci pour ta réponse.

    Citation Envoyé par Médinoc Voir le message
    Pourquoi ne pas utiliser directement une string?
    C'est ce que m'a fait remarquer Arzar

    Faudra que je me trouve la doc "officielle" de la STL avec le descriptif des éléments qu'elle contient, ça m'évitera de faire n'importe quoi...

    Bonne journée

  6. #6
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205

  7. #7
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Bonsoir,

    Goten, merci beaucoup pour les liens. C'est vrai que chercher du côté de SGI ...

    bye

  8. #8
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 174
    Points
    1 174
    Par défaut
    Citation Envoyé par bretus Voir le message
    Bonsoir,

    Goten, merci beaucoup pour les liens. C'est vrai que chercher du côté de SGI ...

    bye
    attention, ce n'est pas la doc officielle, il y a des trucs en plus

  9. #9
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Pour cause, il n'existe pas de doc officielle si ce n'est la norme.. Qui elle est payante .

  10. #10
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Bonjour,

    Même après les corrections suggérées, le problème persistait. Il y a bien une différence de fonctionnement scandaleux entre les deux fonctions std::getline (GCC windows / GCC linux )

    Pour info pour ceux qui rencontrerait le même problème, voilà ce qui semble se passer :

    Sous windows, getline de GCC supprime automatiquement "\r\n" qui correspond aux caractères de fin de ligne.

    Sous linux, getline de GCC supprime que le "\n". Une ligne vide devient donc "\r"...

    Ca laisse vraiment rêveur...

    (On va me dire : "Mais un fichier texte, le \r sert à rien avant le \n en fin de ligne, ça sert à rien"... Ca existe et c'est produit par std::end, les blocs notes, ça doit donc être pris en compte)

    Bye

  11. #11
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 382
    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 382
    Points : 41 588
    Points
    41 588
    Par défaut
    Normalement, sous Linux, il n'y a pas de \r avant un \n.
    Ce bug ne doit donc se présenter que si tu ouvres sous linux un fichier texte créé sous Windows...

  12. #12
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Normalement, sous Linux, il n'y a pas de \r avant un \n.
    Ce bug ne doit donc se présenter que si tu ouvres sous linux un fichier texte créé sous Windows...
    Perdre la compatibilité sur des fichiers textes, pour gagner un test dans une bibliothèque : Du grand stable!

  13. #13
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 174
    Points
    1 174
    Par défaut
    Citation Envoyé par bretus Voir le message
    Perdre la compatibilité sur des fichiers textes, pour gagner un test dans une bibliothèque : Du grand stable!
    sous linux tu es sensé utiliser des fichiers linux..

    tu peux les convertir avec la commande dos2unix

  14. #14
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 382
    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 382
    Points : 41 588
    Points
    41 588
    Par défaut
    De toute façon, de nos jours, avec la diversité des encodages, le principe même de "fichier texte" est compromis. Notamment, pour lire un fichier encodé en UTF-16, il est impossible de le traiter avec des fonctions texte normales, sans recourir à des extensions non-standard (comme le fopen() trafiqué de Microsoft).
    Il devient plus simple de le traiter comme fichier binaire. À ce moment-là, on saute soi-même les \r.

  15. #15
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Il devient plus simple de le traiter comme fichier binaire. À ce moment-là, on saute soi-même les \r.
    Là pour le coup, on fait manger du code au programme... Donc du bloc note! Dites pas honnêtement, ne pas avoir équivalence entre les fonctions de lecture de fichiers, c'est assez minable.

    Et vous aller me dire que quand vous avez une source à compiler, vous vous demander si c'est écrit sous un bloc note windows ou un bloc note linux?

    Il n'y a pas de débat possible, c'est une question de robustesse que vous voulez dans vos programme. S'il faut deviner le comportement des bibliothèques standards, ça devient un cauchemard. Imaginez :
    "Allo service après vente, ca ne lit pas le fichier"
    "-faut pas l'écrire sous windows !"

    bye

  16. #16
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Ne pas avoir la même norme sur tous les systèmes, c'est ça qui idiot. Qu'une lib standard ne mange que les fichiers standards de son système, ça semble logique.

    S'il faut deviner le comportement des bibliothèques standards, ça devient un cauchemard
    Il n'y a rien à "deviner". La bibliothèque standard mange les fichiers standards sur le système où tu es.

    Et comme l'a dit Médinoc, si tu veux faire un programme robuste, oublie la notion de fichier texte et préoccupe toi des encodages et jeux de caractère. Sinon, tu vas droit dans le mur de nos jours.

  17. #17
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 382
    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 382
    Points : 41 588
    Points
    41 588
    Par défaut
    Ce n'est pas le "comportement de la bibliothèque standard": C'est le format de fichiers texte.

    Note que sous FTP, si on transfère en mode texte, le fichier est converti.

  18. #18
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Normal que le code ne se comporte pas de la même manière, il invoque des comportements indéfinis.

  19. #19
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Bonsoir,

    Citation Envoyé par white_tentacle Voir le message
    Il n'y a rien à "deviner". La bibliothèque standard mange les fichiers standards sur le système où tu es.
    C'est sur que tout le monde sait que ça sera "\n" sous windows, que ça sera "\r\n" sous linux pour la fin de ligne... Surtout quand on a pris l'habitude de supprimer seul les deux cas quand la fonction équivalente à getline d'un autre langage ne le fait pas!

    Si vous pensez que nous serons les seuls à tomber dans le piège, j'en doute. Si vous pensez que c'était trop compliqué dans la STL de rechercher le délimiteur parmi "\n", "\r\n", tant mieux.

    Pour moi, soit on supprime tout ce qui ressemble à des caractères de fin de ligne, soit on ne supprime rien. Voilà avec quoi on peut travailler proprement.

    On ne peut pas raisonner simplement avec :
    Si c'est un fichier windows ou que l'on a placé des "\r\n" pour la fin de ligne, sous linux, alors on ne considère comme délimiteur de fin ligne que "\n". Si on travaille sous windows, on considère comme fin de ligne "\r\n"... Il faut donc convertir ces fichiers textes quand on passe d'un système à l'autre...
    Autant dans ce cas, c'est sûr, il faut oublier la notion de fichier texte et surtout la fonction std::getline(...) !

    Si on veut manger de l'UTF-8, on peut bien sûr procéder autrement! Mais là, dans le projet, on s'est limiter à manger de l'ANSI! Le minimum de robustesse, c'est de reconnaître les fins de lignes!

    Vous ne me direz quand même pas que vous êtes en admiration devant ce bloc note windows qui ne reconnaît pas le "\n" seul et met un mignon petit carré! J'ai eu la même impression là dans un bibliothèque pour laquelle j'ai une grande estime malgré tout!

    Si vous trouvez normal d'avoir a gérer des cas pareils pour un si peu gain de temps (éviter au programme lors de l'appel de std::getline(...) de rechercher dans une liste de délimiteurs, que l'on aurait dit standard, la fin d'une ligne) : libre à vous. Personnellement, je pense que c'est plus pour optimiser par absence de contrôle utiles. Un peu comme ce qui a donné naissance au dépassement de buffers où on ne test pas les bornes des tableaux...

    Personnellement, je ne retomberai plus dans le panneau de ces fins de lignes, mais combien d'autres se feront avoir! Je pense que ce genre de comportement des fonctions est source de bugs, qu'on lui trouve des excuses ou non! L'important est que, pour bien des pèlerins, contre toute attente, le comportement d'un même programme avec les mêmes données sera différent!

    La possibilité de mal-entendu, entre l'utilisateur et le concepteur, c'est jamais bon. Je trouve que l'utilisation d'une fonction de haut niveau telle std::getline ne devrait pas conduire l'utilisateur à prendre des considérations systèmes...

    Voilà, après ce n'est que mon opinion. De plus le problème a été résolu par la suppression triviale de l'éventuel "\r" en fin de ligne lors des chargements de fichiers... Pour permettre la lecture d'un fichier en kenji japonais, d'accord, ça serait une autre affaire

    Merci pour votre participation et vos remarques!

    Bye, bonne soirée

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 26/09/2014, 14h08
  2. Comportement différent entre Windows et Linux
    Par Maisondouf dans le forum Interfaces Graphiques en Java
    Réponses: 2
    Dernier message: 05/09/2012, 11h10
  3. Réponses: 5
    Dernier message: 14/11/2010, 23h53
  4. GCC windows linux cross compiler
    Par sybe30 dans le forum Autres éditeurs
    Réponses: 7
    Dernier message: 22/01/2010, 18h48

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