2017-06-22 1 views
5

Note: veuillez lire les commentaires avant de répondre. Le problème semble être spécifique au compilateur.Pourquoi iostream coupe-t-il la première lettre dans certains mots?

J'ai un programme simple qui lit un nom et quelques notes d'un fichier ou de la console dans une structure Student_info, puis imprime certaines des données en surchargeant < < et >> opérateurs. Cependant, le programme coupe des parties ou même des mots entiers et déplace les données. Par exemple, l'entrée

Eunice 29 87 42 33 18 13 
Mary 71 24 3 96 70 14 
Carl 61 12 10 44 82 36 
Debbie 25 42 53 63 34 95 

retours

Eunice: 42 33 18 13 
Mary: 3 96 70 14 
rl: 10 44 82 36 
25: 63 34 95 

suggérant que d'une certaine manière, le flux a ignoré les deux premières lettres de Carl, puis déplacé l'ensemble du flux gauche 1 mot. J'ai essayé de déboguer cela pendant une bonne partie de l'heure, mais cela semble arbitraire. Pour différents noms, différents mots sont coupés, sans motif apparent.

#include <iostream> 
#include <fstream> 
#include <string> 
#include <vector> 

struct Student_info { 
    friend std::ostream &operator<<(std::ostream &output, 
            const Student_info &S) { // overloads operator to print name and grades 
     output << S.name << ": "; 
     for (auto it = S.homework.begin(); it != S.homework.end(); ++it) 
      std::cout << *it << ' '; 
     return output; 
    } 

    friend std::istream &operator>>(std::istream &input, Student_info &S) { // overloads operator to read into Student_info object 
     input >> S.name >> S.midterm >> S.final; 
     double x; 
     if (input) { 
      S.homework.clear(); 
      while (input >> x) { 
       S.homework.push_back(x); 
      } 
      input.clear(); 
     } 
     return input; 
    } 

    std::string name; // student name 
    double midterm, final; // student exam scores 
    std::vector<double> homework; // student homework total score (mean or median) 

}; 


int main() { 
    //std::ifstream myfile ("/Users/.../Documents/C++/example_short.txt"); 
    Student_info student; // temp object for receiving data from istream 
    std::vector<Student_info> student_list; // list of students and their test grades 
    while (std::cin >> student) { // or myfile >> student 
     student_list.push_back(student); 
     student.homework.clear(); 
    } 
    for (auto it = student_list.begin(); it != student_list.end(); ++it) { 
     std::cout << *it << '\n'; 
    } 
    return 0; 
} 

modifier: nouveau caractère ajouté.

Comme vous pouvez le voir ne fonctionne pas avec clang, mais il le fait avec GCC

+3

Lire la ligne entière dans une chaîne avec 'getline()', puis utiliser un 'std :: stringstream' pour le lire dans les membres de la structure. – Barmar

+1

Il fonctionne plutôt bien sur mon ordinateur. – Jiahao

+0

@ArnoldLayne Odd. Je compile en XCode 8.3.3, et [this] (http://imgur.com/a/bxNvU) est la sortie. – JAustin

Répondre

8

Il existe une incohérence entre les implémentations de la façon dont elles implémentent la norme lorsqu'il s'agit d'une entrée formatée en virgule flottante en échec. (Ou plus précisément, libc++) lit et rejette tous les caractères qu'une représentation en virgule flottante valide pourrait contenir, même si elle ne peut pas les contenir à cette position et que la conversion échouera nécessairement.Ces caractères comprennent C et A (les majuscules et les minuscules, car il s'agit de chiffres hexadécimaux et la notation hexadécimale à virgule flottante est en fait autorisée par la norme). Ainsi, lorsque vous essayez de lire un double et que l'entrée contient Carl, les caractères C et A sont lus et supprimés, même si aucun nombre à virgule flottante ne peut commencer avec l'un ou l'autre de ces caractères.

D'autre part, gcc (libstdc++) arrête la lecture dès qu'il est clair que la conversion échouera. Ainsi, si l'entrée contient Carl, la conversion s'arrête au premier caractère (et elle est conservée dans le flux), car un nombre à virgule flottante hexadécimal ne peut pas commencer par C (il doit commencer par 0X).

Je ne donnerai pas d'opinion sur l'implémentation formellement correcte. Quoi qu'il en soit, le code normal devrait éviter les coins subtils et mystérieux de la langue. Si un enregistrement d'étudiant est une ligne, le code devrait lire les lignes. Si un enregistrement d'étudiant est défini comme "un nom et une séquence de nombres qui durent jusqu'au nom suivant", alors arrêtez et lisez this article.

+1

et maintenant nous avons besoin d'un gars aventureux ou gal pour plonger dans la norme et revenir avec une décision sur ce que dit la norme. Belle prise btw. – bolov

+2

@bolov il y a [cette question SO] (https://stackoverflow.com/questions/24689378/characters-extracted-by-istream-double) qui explore ce problème et il y a quelques liens externes intéressants. –

+0

C'est ce que je cherchais. La question liée est géniale! –

1

Je pense que le problème est lors de l'analyse de l'entrée avec le end of line et le double. J'ai trouvé 2 façons de le résoudre:

  1. Lire devoirs jusqu'à la fin de la ligne.

    friend std::istream &operator>>(std::istream &input, Student_info &S) 
    { // overloads operator to read into Student_info object 
        input >> S.name >> S.midterm >> S.final; 
        double x; 
        if (input) 
        { 
        S.homework.clear(); 
        while ((input.peek()!='\n') && input >> x) 
        { 
         //std::cout << x << "\n"; 
         S.homework.push_back(x); 
        } 
        input.clear(); 
        } 
    
        return input; 
    } 
    
  2. Ne pas analyser comme le double si vous connaissez les entrées seraient entiers

    friend std::istream &operator>>(std::istream &input, Student_info &S) 
    { // overloads operator to read into Student_info object 
        input >> S.name >> S.midterm >> S.final; 
        int x; 
        if (input) 
        { 
        S.homework.clear(); 
        while (input >> x) 
        { 
         //std::cout << x << "\n"; 
         S.homework.push_back(x); 
        } 
        input.clear(); 
        } 
    
        return input; 
    } 
    
1

Quand vous savez que l'information pour un objet Student_Info est toujours dans une seule ligne, il est mieux utiliser la stratégie suivante.

  1. Lire une ligne de texte.
  2. Construire un std::istringstream à partir de la ligne de texte.
  3. Extrait les données correspondant à Student_Info du std::istringstream.

Cela rend simplement le code d'analyse plus simple et moins sujette aux erreurs.

// overloads operator to read into Student_info object 
friend std::istream &operator>>(std::istream &input, Student_info &S) 
{ 
    std::string line; 
    if (!getline(input, line)) 
    { 
     // Problem reading a line of text. 
     return input; 
    } 

    std::istringstream str(line); 

    str >> S.name >> S.midterm >> S.final; 
    S.homework.clear(); 
    double x; 
    while (str >> x) 
    { 
     S.homework.push_back(x); 
    } 
    return input; 
} 

FWIW, je ne peux pas reproduire le problème que vous voyez. Découvrez une version fonctionnelle au http://ideone.com/13wHLa.

+0

Je n'ai pas ajouté la prime, mais notez que le problème semble être spécifique au compilateur, car le code fonctionne lorsqu'il est compilé avec gcc, mais pas avec clang. – JAustin