Why does iostream disable the first letter in certain words?

Note: read the comments before replying. The problem seems to be specific to the compiler.

I have a simple program that reads the name and some grades from a file or console into the Student_info structure, and then prints some data, overloading the <and → operators. However, the program disables parts or even whole words and transfers data. For example, input

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 

returns

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

assuming that somehow the stream ignored Karl's first two letters, and then moved the entire stream, leaving 1 word. I tried to debug this for most of the hour, but it seems arbitrary. For different names, different words are cropped without a visible picture.

 #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; } 

edit: added a newline character.

As you can see, it does not work with clang , but it works with GCC

+5
source share
3 answers

There is a mismatch between implementations of how they implement the standard when it comes to failed floating point formatted input.

clang (or rather libc++ ) reads and discards all characters that a valid floating point representation may contain, even if it cannot contain them at this position, and the conversion will necessarily fail. These characters include C and A (both upper and lower case, since they are hexadecimal digits and hexadecimal notes of floating point numbers are actually permitted by the standard). Thus, when trying to read a double , and the input contains Carl , the characters C and A read and discarded, although none of the floating-point numbers can begin with any of these characters.

gcc ( libstdc++ ), on the other hand, stops reading as soon as it becomes clear that the conversion will fail. Thus, if the input contains Carl , the conversion stops at the first character (and is stored in the stream) because the hexadecimal floating-point number cannot start with C (it must start with 0X ).

I will not voluntarily express an opinion on which implementation is formally correct. Be that as it may, a normal code should avoid subtle and mysterious corners of the language. If the student record is a line, then the code should read the lines. If a student’s record is defined as “a name and a sequence of numbers that continue until the next name,” then stop and read this article .

+8
source

I think the problem is analyzing the input with end of line and double . I found two ways to solve this problem:

  • Read the homework to the end of the line.

     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; } 
  • Do not parse it as double if you know the inputs will be integers

     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
source

When you know that the information for the Student_Info object Student_Info always on the same line, it is best to use the following strategy.

  • Read a line of text.
  • Build a std::istringstream from a line of text.
  • Extract data matching Student_Info from std::istringstream .

It just makes the parsing code simpler and less error prone.

 // 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, I cannot duplicate the problem you see. Check out the working version at http://ideone.com/13wHLa .

+1
source

Source: https://habr.com/ru/post/1269086/


All Articles