I'm having a problem reading an integer from a file. As for my knowledge it should work. Can you tell me what have I done wrong here?
int fileCount = 0;
ifstream listFileStream ( fileName );
if ( listFileStream.is_open() ) {
listFileStream >> fileCount;
cout << fileCount;
}
It only prints 0 even though the first line of the file is 28.
You should always check that you read attempt was successful:
if (listFileStream >> fileCount) {
process(fileCount);
}
If the read isn't successful you can try to recover from it or report an error. Here is one way you might try to recover: restore the stream to good state and ignore the first character:
listFileStream.clear();
listFileStream.ignore();
Without restoring the stream to a good state all input attempts would be ignored. Once the offending character is removed you would retry the read.
Related
As far as I know, stream.ignore(n, 'n') should ignore an (n) amount of characters or if ‘\n’ is reached, and skip over to the next line, however, when I run the next code:
// include...
void insertInfo(int info) {
std::fstream stream("infoFile.txt"); // Open the file
while (!stream.eof()) {
std::string a{};
// getline(stream, a); <--- Tried this, didn't work either
stream.ignore(99, '\n');
} // Skip to the last line without any number, in theory
std::cout << info << std::endl; // Check if the output it's correct (Which is)
stream << info; // Insert the info
stream.close(); // Close the file
}
void main() //Main
{
std::cout << "Enter your name, followed by the info you want to add to infoFile:" << std::endl;
std::string info, temp = "";
std::getline(std::cin, temp); // Get the info input
std::stringstream sstream;
sstream << temp;
sstream >> temp >> info; // Remove the name keeping only the info
temp = ""; // ^
std::string::size_type sz;
insertInfo(stoi(info, &sz)); // Convert info string into an integer and insert it in infoFile
}
The console prints out the "info" correct value, however, when I check info.txt, in which I previously wrote a '0' on, you don't see any change.
I tried removing the "ignore" function and it overwrites the 0, which is exactly what I was trying to prevent.
I also tried using "getline" function but the same thing happens.
What is the error here?
Problem
Cannot write to file.
Why
void insertInfo(int info) {
std::fstream stream("infoFile.txt"); // Open the file
Opens file with default permissions, which includes reading. The C++ Standard says I should expect "r+" behaviour and the C Standard says a file opened with "r+" behaviour must exist in order to be read (Someone please add a link if you have one). You cannot create a new file. This is problem 1. The Asker has dealt with this problem by providing a file.
Note: take care when working with files via relative paths. The program's working directory may not be where you think it is. This is problem 1a. It appears that the Asker has this taken care of for the moment.
while (!stream.eof()) {
Common bug. For more details see Why is iostream::eof inside a loop condition considered wrong? In this case since all you're looking for is the end of the file, the fact that the file hasn't been opened at all or has encountered any read errors is missed. Since a file in an error state can never reach the end of the file this quickly becomes an infinite loop. This is problem 2.
std::string a{};
// getline(stream, a); <--- Tryied this, didn't work neither
stream.ignore(99, '\n');
Always test IO transactions for success. This call can fail unchecked.
} // Skip to the last line without any number, in theory
Assuming nothing has gone wrong, and since we're not checking the error state assuming's all we can do, the file has reached the end and is now in the EOF error state. We can't read from or write to the stream until we clear this error. This is problem number 3 and likely the problem the Asker is struggling with.
std::cout << info << std::endl; // Check if the output it's correct (Wich is)
stream << info; // Insert the info
This can fail unchecked.
stream.close(); // Close the file
This is not necessary. The file will be closed when it goes out of scope.
}
Solution
void insertInfo(int info) {
std::fstream stream("infoFile.txt"); // Open the file
while (!stream.eof()) {
stream.ignore(99, '\n');
} // Skip to the last line without any number, in theory
std::cout << info << std::endl; // Check if the output it's correct (Wich is)
stream.clear(); // Added a call to clear the error flags.
stream << info; // Insert the info
stream.close(); // Close the file
}
Now we can write to the file. But let's improve this shall we?
void insertInfo(int info) {
std::fstream stream("infoFile.txt");
while (stream.ignore(99, '\n')) // moved ignore here. now we ignore, then test the result
{
}
stream.clear();
stream << info << '\n'; // added a line ending. Without some delimiter the file
// turns into one big number
}
Note that this isn't exactly kosher. If any ignore fails for any reason, we bail out and possibly write over data because the code blindly clears and writes. I'm not spending much time here trying to patch this up because we can get really, really simple and solve the problem of creating a non-existent file at the same time.
void insertInfo(int info) {
std::fstream stream("infoFile.txt", std::ios::app);
stream << info << '\n';
}
Two lines and pretty much done. With app we append to the file. We do not need to find the end of the file, the stream automatically points at it. If the file does not exist, it is created.
Next improvement: Let people know if the write failed.
bool insertInfo(int info) {
std::fstream stream("infoFile.txt", std::ios::app);
return static_cast<bool>(stream << info << '\n');
}
If the file was not written for any reason, the function returns false and the caller can figure out what to do. The only thing left is to tighten up the stream. Since all we do is write to ti we don't need the permissiveness of a fstream. Always start with the most restrictive and move to the least. This helps prevent some potential errors by making them impossible.
bool insertInfo(int info) {
std::ofstream stream("infoFile.txt", std::ios::app);
return static_cast<bool>(stream << info << '\n');
}
Now we use an ofstream and eliminate all the extra overhead and risk brought in by the ability to read the stream when we don't read the stream.
I'm trying to read in a file that should contain only numbers in it. I can successfully read in the entire file if it meets that criteria, but if it so happened to have a letter in it, I need to return false with an error statement.
The problem is I'm finding it hard for my program to error when it finds this character. It can find it no problem, but when it does, it decides to just skip over it.
My code to read in the file and attempt to read in only numbers:
bool compute::Read (ifstream& stream)
{
double value;
string line;
int lineNumber = 1;
if (stream)
{
while (getline(stream, line))
{
lineNumber++;
istringstream strStream(line);
while (strStream >> value)
{
cout << value << endl;
}
}
}
return true;
}
The input file which I use for this is
70.5 61.2 A8 10.2
2
Notice that there is a non-number character in my input file. It should fail and return false at that point.
Currently, all it does is once it hits the "A", it simply returns to the next line, continuing the getline while loop.
Any help with this would be much appreciated.
The stringstream does catch those errors, but you're doing nothing to stop the enclosing loop from continuing when an error is found. You need to tailor your main loop so that it stops when the stringstream finds an error, which you can't do if the stringstream is being reconstructed on each iteration. You should create a for() loop instead and construct the stringstream in the declaration part. And the condition to the loop should be "as long as the stringstream and stream do not catch an error". For example:
for (std::istringstream iss; iss && std::getline(stream, line);)
{
iss.clear();
iss.str(line);
while (iss >> value)
{
std::cout << value << '\n';
}
}
Futhermore, it doesn't look like you need to use std::getline() or std::istringstream if you just want to print each value. Just do:
while (stream >> value) {
std::cout << value << '\n';
}
The above will stop when it finds an invalid character for a double.
You need the code to stop streaming but return false if it hasn't yet reached the end of the "input".
One way, possibly not the most efficient but still one way, to do that is parse a word at a time.
If you read first into a std::string and if it works (so the string is not empty) create an istringstream from that string, or reuse an existing one, and try streaming that into a double value.
If that fails, you have an invalid character.
Of course you can read a line at a time from the file, then split that into words, so that you can output a meaningful error message showing what line the bad text was found.
The issue of reading straight into doubles is that the stream will fail when it reaches end of file.
However it is possible to workaround that too because the reason for failing has an error status which you can check, i.e. you can check if it eofbit is set. Although the f in eofbit stands for "file" it applies to any stream not just files.
Although this method may sound better than reading words into a string first, I prefer that method in normal circumstances because you want to be able to report the error so you'll want to print in the error what was read.
First of all, there's an data inside my file...
1|Malaysia|UK|One-Way|20|6|20|6|2000|12|12|12|12|12|
The above data is the data inside my file
But when I cout the data, there's an duplicate data...
1|Malaysia|UK|One-Way|20|6|20|6|2000|12|12|12|12|12|
1|Malaysia|UK|One-Way|20|6|20|6|2000|12|12|12|12|12|
So what's the problem when I cout the data???
Here's the code....
void Flight::displayFlight(vector <Flight> &flightProfile)
{
string readFlightID, readPrice, readBusinessList, readBusinessWaitingList, readEconomicList, readEconomicWaitingList;
flightProfile.erase(flightProfile.begin(),flightProfile.end());
ifstream inFlight("Flight.txt");
if(inFlight.fail()) return;
while(!(inFlight.eof()))
{
getline(inFlight,readFlightID,'|');
istringstream(readFlightID)>>flightID;
getline(inFlight,departure,'|');
getline(inFlight,destination,'|');
getline(inFlight,flightType,'|');
getline(inFlight,readBusinessList,'|');
istringstream(readBusinessList)>>businessList;
getline(inFlight,readBusinessWaitingList,'|');
istringstream(readBusinessWaitingList)>>businessWaitingList;
getline(inFlight,readEconomicList,'|');
istringstream(readEconomicList)>>economicList;
getline(inFlight,readEconomicWaitingList,'|');
istringstream(readEconomicWaitingList)>>economicWaitingList;
getline(inFlight,readPrice,'|');
istringstream(readPrice)>>price;
getline(inFlight, day,'|');
getline(inFlight, month,'|');
getline(inFlight, year,'|');
getline(inFlight, hour,'|');
getline(inFlight, min,'|');
inFlight.ignore(numeric_limits<streamsize>::max(), '\n');
cout<<flightID<<departure<<destination<<flightType<<businessList<<businessWaitingList<<economicList<<economicWaitingList<<price<<day<<month<<year<<hour<<min<<endl;
}
inFlight.close();
}
Your (and others) common mistake is that the eof-bit is only set on input operations (RTFM).
The correct way to read a file line by line would be to do:
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
cout << line << endl;
}
myfile.close();
}
You always need to check after you read from a stream if the stream is in a good state! If reading of the data failed, e.g., because the end of the stream was reached, the stream will be in a fail state. For example
while (std::getline(inFlight, readFlightID)
&& std::istringstream(readFlightId) >> flightID
&& std::getline(inFlight, departure)
...
) {
// now process the read data
}
BTW, note that the trick using a temporary stream only works like this if the target type is a member of std::istream. If it is not, you'll need to extra a reference from the stream, e.g. using
std::istringstream("some text") >> std::skipws >> value
That's because you're not checking that your getline succeeds.
The last time through the loop, it probably fails (because
you've read all of the data), so you pick up the old values.
This is not the way to read line based input. For starters,
line based input should be read line by line:
std::string line;
while ( std::getline( inFlight, line ) ) {
// Parse line here...
}
There are many ways to parse the line. One of the most common
solutions is to put it into an std::istringstream and read
from that. That's probably overkill for what you're doing,
however; you need probably something like boost::split (which
you can knock up in less than an hour if you can't use Boost).
At any rate, while ( !someIStream.eof() ) is never correct.
Two other quick comments: you shouldn't define your variables
before you need them, and there's no real point in closing
inFlight if it's immediately going out of scope.
Why does ifstream set the failbit to 1 after reading the last line of the specified file? How do I know if the specified file has been read correctly?
bool read_csv_file(const char* filename, vector<string>& lines, bool adding = false)
{
if( !adding ) lines.clear();
ifstream csvfile;
csvfile.open(filename);
if( csvfile.is_open() )
{
string line;
while( csvfile.good() && getline(csvfile,line) )
{
lines.push_back(line);
cout << "fail: " << csvfile.fail() << endl;
}
cout << "fail: " << csvfile.fail() << endl;
csvfile.close();
return (!csvfile.fail());
}
return false;
}
The fail bit is set after you run off the end of the file. Once that happens, you mustn't attempt to interpret the result of your input operation. That's perfectly fine, though, and getline will not set the fail bit while there's still any data to be read. So the following standard loop extracts all the lines:
for (std::string line; std::getline(csvfile, line); )
{
// process "line"
}
// all done
The only reason failbit could be set after reading the last
line (or any line) is if there were an error in the library, and
I don't really believe it. If failbit is set, it means that
you didn't read anything. In your case, it will never be set
when you're in the loop; if it were set, getline would have
evaluated to false, and you wouldn't have entered the loop.
And of course, the loop terminates precisely because getline
fails (or would fail—normally, you would not test for
good before doing input, and if you do, consider that the
failbit was set, regardless, if the test fails).
The usual pattern for this sort of thing is:
while ( someInput ) {
// ...
}
if ( csvfile.bad() ) {
// Serious error (disk read error, etc.)...
} else if ( ! csvfile.eof() ) {
// Formatting error...
} else {
// Normal end of file...
}
When someInput is std::getline(), however, you will never
fail because of a formatting error, so the else if above will
never be true (and a lot of code treats hard disk errors as if
they were an end of file, and so ignores the if part as well).
Too check for erroneous reads, you must test badbit using stream.bad().
Failbit indicates failure in operation logic, and apparently getline sets it when reaching EOF (confirmed on my machine).
#include <iostream>
#include <fstream>
int main() {
std::fstream inf( "ex.txt", std::ios::in );
while( !inf.eof() ) {
std::cout << inf.get() << "\n";
}
inf.close();
inf.clear();
inf.open( "ex.txt", std::ios::in );
char c;
while( inf >> c ) {
std::cout << c << "\n";
}
return 0;
}
I'm really confused about eof() function. Suppose that my ex.txt's content was:
abc
It always reads an extra character and shows -1 when reading using eof(). But the inf >> c gave the correct output which was 'abc'? Can anyone help me explain this?
-1 is get's way of saying you've reached the end of file. Compare it using the std::char_traits<char>::eof() (or std::istream::traits_type::eof()) - avoid -1, it's a magic number. (Although the other one is a bit verbose - you can always just call istream::eof)
The EOF flag is only set once a read tries to read past the end of the file. If I have a 3 byte file, and I only read 3 bytes, EOF is false, because I've not tried to read past the end of the file yet. While this seems confusing for files, which typically know their size, EOF is not known until a read is attempted on some devices, such as pipes and network sockets.
The second example works as inf >> foo will always return inf, with the side effect of attempt to read something and store it in foo. inf, in an if or while, will evaluate to true if the file is "good": no errors, no EOF. Thus, when a read fails, inf evaulates to false, and your loop properly aborts. However, take this common error:
while(!inf.eof()) // EOF is false here
{
inf >> x; // read fails, EOF becomes true, x is not set
// use x // we use x, despite our read failing.
}
However, this:
while(inf >> x) // Attempt read into x, return false if it fails
{
// will only be entered if read succeeded.
}
Which is what we want.
The EOF flag is only set after a read operation attempts to read past the end of the file. get() is returning the symbolic constant traits::eof() (which just happens to equal -1) because it reached the end of the file and could not read any more data, and only at that point will eof() be true. If you want to check for this condition, you can do something like the following:
int ch;
while ((ch = inf.get()) != EOF) {
std::cout << static_cast<char>(ch) << "\n";
}
iostream doesn't know it's at the end of the file until it tries to read that first character past the end of the file.
The sample code at cplusplus.com says to do it like this: (But you shouldn't actually do it this way)
while (is.good()) // loop while extraction from file is possible
{
c = is.get(); // get character from file
if (is.good())
cout << c;
}
A better idiom is to move the read into the loop condition, like so:
(You can do this with all istream read operations that return *this, including the >> operator)
char c;
while(is.get(c))
cout << c;
eof() checks the eofbit in the stream state.
On each read operation, if the position is at the end of stream and more data has to be read, eofbit is set to true. Therefore you're going to get an extra character before you get eofbit=1.
The correct way is to check whether the eof was reached (or, whether the read operation succeeded) after the reading operation. This is what your second version does - you do a read operation, and then use the resulting stream object reference (which >> returns) as a boolean value, which results in check for fail().