Recover ifstream from failed read - c++

I currently have a piece of code that reads in text from a file using an ifstream. Each line of this file corresponds to a different piece of data that must be encoded into a struct. My "encodeLine" function takes care of this.
For safety, I want my system to be able to handle data that is too big to fit into its variable. For example, if the number 999999999 is read into a short, I want the program to be able to continue on reading the rest of the lines.
Currently, when I encounter data like this, I print out "ERROR" and clear the stream. However, when I perform more reads, the data that is read is corrupted. For example, on the next line the number "1" should be read, but instead something like 27021 is read.
How can the ifstream be reset to continue with valid reads?
Here is my code:
ifstream inputStream;
inputStream.open(foo.txt);
char token[64];
int totalSize = 0;
// Priming read
inputStream >> token;
while(inputStream.good())
{
// Read and encode line of data from file
totalSize = totalSize + encodeLine(inputStream, &recordPtr, header, filetypeChar)
if(!inputStream.eof() && !inputStream.good())
{
printf("ERROR");
inputStream.clear();
}
else if(inputStream.eof())
{
break;
}
inputStream >> token;
}

Related

How to signal EOF on a file the program opens

How would I signal an EOF when reading in a file in C++? I'm writing a direct coded scanner, as a part of a compiler design, that reads in a file and splits it up into tokens for a language.
I am to read in the whole program, strip out the comments, and compress the whitespace. Then put the resulting program char by char into a buffer with max size of 1024 chars. So when we empty we will refill the buffer or what not.
To open the file I have this written:
// Open source file.
source_file.open (filename);
if (source_file.fail()) {
// Failed to open source file.
cerr << "Can't open source file " << *filename << endl;
buffer_fatal_error();
To fill the buffer, I am wanting to use a while loop and iterate like
int i = 0;
// Iterate through the whole file
while(source_file.at(i) != EOF)
{
// If not a tab or newline add to buffer
if (source_file.at(i) != "\n" || source_file.at(i) != "\t")
{
bufferList.add(source_file.at(i));
}
i++;
}
Would there be a way to signal EOF like that for the file that I am opening?
This is more or less a general outline for what to do. I will need to figure out how to refill the buffer once I am empty or to use dual buffering. I also need to figure out how to strip out a comment which would begin with #. For instance # This is a comment. My scanner would see # and remove everything after that until it gets the the next newline char.
Try this:
char c;
std::vector<char> buffer(1024);
while (source_file.get(c))
{
if ((c != '\n') || (c != '\t'))
{
buffer.push_back(c);
}
}
The standard method for reading data is to test for the result of the read operation in a while loop.
For block reading, you could do something like this:
char buffer[1024];
while (source_file.read(buffer, sizeof(buffer))
{
// Process the buffer here
}
You should also use std::istream::gcount() to get the number of characters read from the file, as it could be less than the buffer size.

Duplicate Data when I retrieve the data from file in C++

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.

How to skip a string when reading a file line by line

I have managed to skip the name section when reading values from a file with name and value pairs. But is there another way to skip the name section without declaring a dummy string to store the skipped data?
Example text file: http://i.stack.imgur.com/94l1w.png
void loadConfigFile()
{
ifstream file(folder + "config.txt");
while (!file.eof())
{
file >> skip;
file >> screenMode;
if (screenMode == "on")
notFullScreen = 0;
else if (screenMode == "off")
notFullScreen = 1;
file >> skip;
file >> playerXPosMS;
file >> skip;
file >> playerYPosMS;
file >> skip;
file >> playerGForce;
}
file.close();
}
You can use std::cin.ignore to ignore input up to some specified delimiter (e.g., a new-line, to skip an entire line).
static const int max_line = 65536;
std::cin.ignore(max_line, '\n');
While many people recommend specifying a maximum of something like std::numeric_limits<std::streamsize>::max(), I do not. If the user accidentally points the program at the wrong file, they shouldn't wait while it consumes an inordinate amount of data before being told something's wrong.
Two other points.
Don't use while (!file.eof()). It mostly leads to problems. For a case like this, you really want to define a struct or class to hold your related values, define an operator>> for that class, then use while (file>>player_object) ...
The way you're reading right now really tries to read a "word" at a time, not a whole line. If you want to read a whole line, you probably want to use std::getline.

C++ line jumping and file reading

I have a file with numbers in it. I would like to read certain lines (ones that haven't been read already but are no long easily accessible due to the way my code runs)
for example..
I have code like
for (c=0; c < 5;c++)
{
in >> tmp;
}
when implemented this reads 5 parts of the first line (lines are all the same length).
I want to be able to call this same section of code again and be able to read the second..third.ect
what do I need to do to make this work
Assuming in is an input stream (istream), you can use its seekg method in order to seek back to the beginning of the file.
// read it the first time
for (c=0; c < 5;c++)
{
in >> tmp;
}
in.seekg(0, in.beg); // seek to the beginning
// read it the second time
for (c=0; c < 5;c++)
{
in >> tmp;
}
Check out the documentation of the seekg method.

c++ text decoder decoding more than asked for

Im working on a text file decoder along with an encoder, they work off of two different text files. The decoder prints the decoded message underneath the encoded message but it also prints a bunch of other stuff as well. How do i fix this
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
ifstream fin; // input file
string line;
ofstream fout;
//open output file
fout.open("secret.txt", ios::app);
if (!fout.good()) throw "I/O error";
// open input file
fin.open("secret.txt");
if (!fin.good()) throw "I/O error";
// read input file, decode, display to console
while (fin.good()) {
getline(fin, line);
for (int i = 0; i < line.length(); i++) // for each char in the string...
line[i]--; // bump the ASCII code down by 1
fout << line << endl; // display on screen
}
// close file
fin.close();
return 0;
}
the text file from the encoder reads
Uftujoh234
Ifmmp!nz!obnf!jt!cpc
Dmptfe!
Uftujoh
which decodes to
Testing123
Hello my name is bob
Closed
Testing
this is all the extra stuff it also prints in the text file
Sdrshmf012
Gdkknlxm`ldhrana
Bknrdc
Sdrshmf
Rcqrgle/01
Fcjjmkwl_kcgq`m`
Ajmqcb
Rcqrgle
Qbpqfkd./0
Ebiiljvk^jbfp_l_
#ilpba
Qbpqfkd
Paopejc-./
Dahhkiuj]iaeo^k^
?hkoa`
Paopejc
O`nodib,-.
C`ggjhti\h`dn]j]
>gjn`_
O`nodib
N_mncha+,-
B_ffigsh[g_cm\i\
=fim_^
N_mncha
M^lmbg`*+,
A^eeh
The extra data you see is actually valid output from decoding the data in "secret.txt".
I'm not sure if this is what you want, but are you aware that you are reading and writing to the same file each time you run your application?
You'll append more and more "decoded" data to the file, and therefore you get the extra output you are referring to.
Also, there is an issue with your while-loop.
fin.good () will remain true until some of the error bits has been set inside of fin, though it will enter the loop one time too much since you should check to state of the stream immediately after your call to getline (fin, ...).
Currently the reading will fail but you will still process the "unread" data.
std::getline will return the stream object, and since a std::istream (as well as std::ostream) implicitly can be converted to a boolean to check it's current state you should use that as your loop-condition.
Change your loop into something as the below and see if that solves your problem.
while (getline (fin, line))
{
for (int i = 0; i < line.length(); i++) // for each char in the string...
line[i]--; // bump the ASCII code down by 1
fout << line << endl; // display on screen
}
The extra stuff isn't extra. You are writing data into the same file you are reading, so what you do is:
write line
read line
You are renencoding the data you already encoded.