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.
Related
So the while loop at the bottom of this following code simply just loops continuously I had it working before I added the line
questionFile >> answersArray[i];
This line seems to break it so that nothing writes into the questionsArray anymore, I'm rather stumped by this so help would be appreciated.
ifstream questionFile;
int i = 0;
switch (x){
case 1:
questionFile.open("Topic1 Questions.txt", ios::app);
break;
case 2:
questionFile.open("Topic2 Questions.txt", ios::app);
break;
case 3:
questionFile.open("Topic3 Questions.txt", ios::app);
break;
case 4:
questionFile.open("Topic4 Questions.txt", ios::app);
break;
}
if (!questionFile)
{
cout << "Cannot load file" << endl;
}
else
{
if (questionFile.peek() != ifstream::traits_type::eof()) {
while (!questionFile.eof())
{
getline(questionFile, questionsArray[i]);
questionFile >> answersArray[i];
i++;
}
}
questionFile.close();
}
Both getline and operator>> extract from the file but getline reads until '\n' (or any character you specify) while operator>> reads until a whitespace. Furthermore, operator>> will leave the '\n' in the stream causes the next invocation of getline to read nothing.
Read the getline and operator>> documentations for more details (those are links).
Also, post the format that the questions/answers are stored in the file because how your code will run is very dependent on that. If you're just expecting alternating lines of questions and answers, just use getline and forget the operator.
Hopefully this helps.
It's a little hard (okay, impossible, really) to be certain what your problem is without seeing things like the type of answersArray. It would also help (a lot) to see what a sample of the input file you want to read.
For the moment, I'm assuming your input file look at least a little like this:
1) air 2) earth 3) fire 4) water
3
1) Solid 2) Liquid 3) Gas 4) Plasma
2
i.e., one line is a string (containing questions or possible answers, or maybe both), and the next line is a single number signifying the correct answer (or something on that order).
Assuming that's the case, I'd read all the data from the input file itself using std::getline. Then I'd split that up into the necessary pieces.
I'd probably also create an answer class (or something similar) to hold both the string and the number, and overload operator>> for that class to read both.
struct answer {
std::string question;
int answer;
friend std::istream &operator>>(std::istream &is, answer &a) {
std::getline(is, a.question);
std::string temp;
std::getline(is, temp);
a.answer = boost::lexical_cast<int>(temp);
return is;
}
};
Using that, reading the data from the file could look something like this:
std::vector<answer> answers{
std::istream_iterator<question>(questionFile),
std::istream_iterator<question>()};
This reads the data from the file, and puts the items it reads into the vector I've named answers.
While we're at it, I'd also change the code to open the file, at least a little bit. ios::app only really makes sense if you're going to write to a file, not just read from it. Second, a case statement to choose hard-coded file names seems a bit clumsy, at least to me.
I'd probably write that part of the code more like this:
if (x<0 || x > 4)
throw std::runtime_error("prohibited file name");
std::stringstream name << "Topic" << x << " Questions.txt";
std::ifstream questionFile(name.str());
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.
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.
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.
I am implementing a very simple file database. I have 2 basic operations:
void Insert(const std::string & i_record)
{
//create or append to the file
m_fileStream.open(m_fileName.c_str(), std::ios::out | std::ios::app);
if (m_fileStream.is_open())
{
m_fileStream << i_record << "\n";
}
m_fileStream.flush();
m_fileStream.close();
}
/*
* Returns a list with all the items in the file.
*/
std::vector<std::string> SelectAll()
{
std::vector<std::string> results;
m_fileStream.open(m_fileName.c_str(), std::ios::in);
std::string line;
if (m_fileStream.is_open())
{
while (!m_fileStream.eof())
{
getline (m_fileStream, line);
results.push_back(line);
}
}
m_fileStream.close();
return results;
}
the class has m_fileStream and m_fileName as private members.
OK - here's the problem:
If I do something like:
db->Insert("a");
db->SelectAll();
db->Insert("b");
The end result is that the file will contain only "a"; WHY?
NOTE: it seems that getline() will set the fail bit. but why?
Change
while (!m_fileStream.eof())
{
getline (m_fileStream, line);
results.push_back(line);
}
to
while (getline (m_fileStream, line))
{
results.push_back(line);
}
Otherwise you will get one additional empty line at the end. eof() will return true only once you tried to read past the end of the file, and not if only the next read would be past the end of the file.
It sets the failbit because getline tries to extract characters from the stream. If there are no characters left (and no '\n' has been seen yet), stream.get(c) to a character will set the failbit. Then getline will set the eofbit and then .eof() will return true, and your loop exits.
If you don't want failbit set, then change your condition from !stream.eof() to stream.peek() != EOF (and make sure there is a trailing newline in your file).
This now is also the solution to your problem: .close() doesn't .clear() your stream, so the failbit still is set if you reopen your file. call stream.clear() after reading your stuff in, and then it works.
I think litb pretty much nailed it. But just to add my $0.02:
1) I always favored:
while ( stream && (stream.peek() != EOF) ) {...}
As [bad] events other than EOF can occur.
(And, as mentioned by litb, peek()!=EOF gets around the problem of stream not setting EOF until we try to read past the end.)
.
2) Since "m_fileStream" is open'ed, read/written/flushed, and closed in both these methods...
Why not declare it locally on the stack? Doing so assures that no previous state issues remain behind to mess you up. And yer accessing the disk, so efficiency may not be the largest concern...
Besides, you can be lazy:
ifstream stream ( m_fileName.c_str() );
ASSERT( stream, !=, NULL ); // Uses my own ASSERT macro && stream.operator().
while ( stream && (stream.peek() != EOF) ) {...}