I have this text sample
Ahmed 10
kmal 5
doola 6
And I am using this code to read it
if (myfile.is_open())
{
while ( myfile.good() )
{
myfile >> name;
myfile >> phone;
cout << name <<" "<<phone<<endl;
}
myfile.close();
}
I get this output
Ahmed 10
kmal 5
doola 6
doola 6
Why does this code read the last line twice ?
myfile.good() becomes false AFTER the read fails. Same with myfile.eof().
What you want is basically:
myfile >> name;
myfile >> phone;
if (!myfile.good()) break;
And this can be shortened to:
if (!(myfile >> name >> phone)) break;
or finally:
while (myfile >> name >> phone) { ... }
Try
while( myfile >> name >> phone ) {
// your code here
}
I believe the problem with the other approach is that eof isn't signaled until you actually try to read more than you should. That is, when you attempt to myfile >> name on the last round. That fails, as does myfile >> phone and only then do you break out of the loop.
Don't use while(myfile.good()). It will still say true, even when you have read the last line in your file. And then the next >> will fail, leaving the name and phone variables unchanged from the last input. Use this instead: while(myfile >> name >> phone) {...}
iostream status is not predictive. It reports the results of the
previous operation, not of the next (which would be impossible to
implement anyway). And myfile.good() isn't even a reliable indicator
of the results of the previous operation: if the operation failed, it
will be false, but if the operation succeeded, it's not necessarily
true. Forget that function.
Use the fact that the stream can act as a boolean to test success of the
previous operation. Once you have failure, you can then use eof(),
and bad() to determine why (but with regards to eof(), only after
failure---eof() may return true even if the previous operation
succeeded). Thus:
while ( myfile >> name >> phone ) {
// ...
}
if ( myfile.bad() ) {
std::cerr << "Hardware read error" << std::endl;
} else if ( !myfile.eof() ) {
std::cerr << "Format error in file" << std::endl;
}
Related
I am trying to read in the data in a names.txt file and output the full name and ideal body weight for each person. Using a loop to read the names and feet and inches of each person from the file.
The file reads:
Tom Atto
6
3
Eaton Wright
5
5
Cary Oki
5
11
Omar Ahmed
5
9
I'm using the following code for this:
string name;
int feet, extraInches, idealWeight;
ifstream inFile;
inFile.open ("names.txt");
while (getline(inFile,name))
{
inFile >> feet;
inFile >> extraInches;
idealWeight = 110 + ((feet - 5) * 12 + extraInches) * 5;
cout << "The ideal weight for " << name << " is " << idealWeight << "\n";
}
inFile.close();
when i run this im getting output:
The ideal weight for Tom Atto
is 185
The ideal weight for
is -175
Add this statement in while loop after reading the two extraInches value.
inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
It ignores the '\n' after the second integer you read in while loop. You may refer: Use getline and >> when read file C++
You are running into problems because after the line
inFile >> extraInches;
is executed in the first iteration of the loop, there is still a newline character in the stream. The next call to getline simply returns an empty line. The subsequent call
inFile >> feet;
fails but you don't check whether the call was successful.
Couple of things I want to mention in relation to your problem.
Mixing unformatted input, using getline, and formatted input, using operator>> is fraught with problems. Avoid it.
To diagnose IO related problems, always check the state of the stream after an operation.
In your case, you can use getline to read lines of text, and then use istringstream to extract numbers from the lines.
while (getline(inFile,name))
{
std::string line;
// Read a line of text to extract the feet
if ( !(inFile >> line ) )
{
// Problem
break;
}
else
{
std::istringstream str(line);
if ( !(str >> feet) )
{
// Problem
break;
}
}
// Read a line of text to extract the inches
if ( !(inFile >> line ) )
{
// Problem
break;
}
else
{
std::istringstream str(line);
if ( !(str >> inches) )
{
// Problem
break;
}
}
idealWeight = 110 + ((feet - 5) * 12 + extraInches) * 5;
cout << "The ideal weight for " << name << " is " << idealWeight << "\n";
}
I am trying to learn file i/o in c++. I have a bunch of variables like kill, death, assist, result, notes and I want to enter them into a text file. I have seen tutorials where they use cin inside a while loop like
while ( cin >> kill >> death >> assist ) {
file << kill << death << assist ; }
but I want the user to type in "done" when they are done entering their input.
I know in python you can do something like
while (input != "done" ):
//code//
can I do something similar?
while ( cin >> () != "done" ) {
cin >> kill >> death >> assist >> notes ;
file << kill << death << assist << notes ; }
You can have multiple conditions using the logical AND and OR operators.
You can also use std::getline to read a line, and then use an input string stream to parse the input.
For example something like:
std::string input;
while (std::getline(cin, input) && input != "done")
{
std::istringstream is(input);
is >> kill >> death >> assist;
...
}
The expression in the condition reads a while line, checks that there was no errors or end-of-file when reading, and then check that the input was not "done".
In my simple Fraction class, I have the following method to get user input for the numerator, which works fine for checking garbage input like garbage, but will not recognize user input which starts with an integer, and is followed by garbage, 1 garbage or 1garbage.
void Fraction::inputNumerator()
{
int inputNumerator;
// loop forever until the code hits a BREAK
while (true) {
std::cout << "Enter the numerator: ";
// attempt to get the int value from standard input
std::cin >> inputNumerator;
// check to see if the input stream read the input as a number
if (std::cin.good()) {
numerator = inputNumerator;
break;
} else {
// the input couldn't successfully be turned into a number, so the
// characters that were in the buffer that couldn't convert are
// still sitting there unprocessed. We can read them as a string
// and look for the "quit"
// clear the error status of the standard input so we can read
std::cin.clear();
std::string str;
std::cin >> str;
// Break out of the loop if we see the string 'quit'
if (str == "quit") {
std::cout << "Goodbye!" << std::endl;
exit(EXIT_SUCCESS);
}
// some other non-number string. give error followed by newline
std::cout << "Invalid input (type 'quit' to exit)" << std::endl;
}
}
}
I saw a few posts on using the getline method for this, but they didn't compile when I tried them, and I'm having trouble finding the original post, sorry.
Better check as follows:
// attempt to get the int value from standard input
if(std::cin >> inputNumerator)
{
numerator = inputNumerator;
break;
} else { // ...
Or yes: Follow recommendations to parse a complete input line combining std::getline() and std::istringstream appropriately.
I ask the user for an integer input and I do not want to execute code unless it is strictly an integer.
int x;
if(cin >> x)
For instance if the user inputs a double above, the if statement will execute with implicit conversion to an integer. Instead I don't want the code to execute at all.
How can I prevent this?
There is no conversion there. If the user enters a fraction (there is no double), then the >> extraction stops at the decimal point.
http://ideone.com/azdOrO
int main() {
int x;
std::cin >> x;
std::cout << std::cin.rdbuf();
}
input:
123.456
output:
.456
If you want to flag the existence of the decimal point as an error, you will have to do something to extract it from cin and detect it.
One good parsing strategy with C++ streams is to getline what you know you will process into an istringstream, call it s, then check that that s.peek() == std::char_traits<char>::eof() when you finish. If you don't use getline to pull the individual number, then peek can check whether the next character is a space (using std::isspace) without consuming that character from the stream.
Probably the cleanest way to check that input is finished, although it's somewhat esoteric, is to use std::istream::sentry.
if ( ! ( std::cin >> x ) || std::istream::sentry( std::cin ) ) {
std::cerr << "Invalid or excessive input.\n";
}
This consumes space at the end of the input. sentry also provides a noskipws option to avoid consuming the space.
if ( ! ( std::cin >> x ) || std::istream::sentry( std::cin, true ) ) {
std::cerr << "Invalid or excessive input. (No space allowed at end!)\n";
}
This seems to work. It ignores whitespace, I don't know if that's ok with you.
string s;
cin >> s;
stringstream ss(s);
int x;
if (! (ss >> x))
{
cerr << "You didn't enter an integer." << endl;
return -1;
}
string temp;
ss >> temp;
if (! temp.empty())
{
cerr << "You didn't enter an integer." << endl;
return -1;
}
SO when my program starts, it attempts to read a list of products from a file. but if the file does not exist it displays an error and continue on. the problem im having is when it displays the error, it doesnt continue on to the do while loop
ifstream input;
input.open("data.txt");
if (input.fail())
{
cout << "\n Data file not found \n";
}
ListItemType data;
input >> data.productname;
while(( !input.eof()))
{
input >> data.category;
input >> data.productprice;
addproduct(head, data);
input >> data.productname;
}
input.close();
It's not identical functionality, but it's generally better to move towards something like:
if (std::ifstream input("data.txt"))
{
ListItemType data;
while (input >> data.productname >> data.category >> data.productprice >> data.productname)
addproduct(head, data);
if (!input.eof())
std::cerr << "Error parsing input file.\n";
}
else
cout << "\n Data file not found \n";
If you structure your if/else clauses as above, whatever happens it will continue to the following code as you'd like.
Note that the code above checks for a problem after each input operation. Your code tries to read data.productprice even if reading data.category failed. It's kind of weird you're reading productname twice, and I'm assuming you can call addproduct after the I/O - if not you'll need a while loop like:
while (input >> data.productname >> data.category >> data.productprice)
{
addproduct(head, data);
if (!(input >> data.productname))
break;
}