outputting from a file stuck in loop and not reading? - c++

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());

Related

Reading from a file with getline never goes past the first line

I am trying to read the contents of an obj file in C++, and I am struggling with it. It's been a couple of years since I have done anything with C++, so I have forgotten a lot, but I am much more proficient in Java.
I am trying to read an obj file with contents like this
# This file uses centimeters as units for non-parametric coordinates.
mtllib crayon.mtl
g default
v 0.927930 -17.363054 -0.279038
...
vt 0.648603 0.107966
... etc
However, it doesn't seem to be able to read past the first line. Here is my code thus far:
void TextureMapper::read_file(string file_name)
{
ifstream file;
file.open(file_name);
if (file.is_open())
{
string line;
while (getline(file, line))
{
std::stringstream ss(line);
string token;
ss >> token;
switch (get_obj_type(token))
{
case GEOMETRIC_VERTEX:
break;
case TEXTURE_VERTEX:
break;
case VERTEX_NORMAL:
break;
case GROUP_NAME:
break;
case SMOOTHING_GROUP:
break;
case FACE:
break;
case MATERIAL_NAME:
break;
case MATERIAL_LIBRARY:
break;
case COMMENT:
break;
case BLANK:
break;
}
}
file.close();
}
else
std::cerr << "Couldn't open " << file_name << " for reading\n";
}
Where the enums (GEOMETRIC_VERTEX, GROUP_NAME, etc) are mapped to the parts of the obj file (like v, g vt, #, etc).
EDIT I realized the blank line in my file might be causing the exception, so I added an enum for that, and added it as a case to my switch statement, and now I don't get an exception, but it just never reads past the first line.
The first line (# This file uses centimeters as units ...) is read in properly, and I can see that when I debug it. However, when it re-enters the while loop again, it looks like it re-reads that same line, again and again.
I'm unsure what I am doing incorrectly when I read in this file. Let me know if you need more information to go on.

c++ ifstream Skipping Data

I'm trying to get my program to read two lines out of a six line data file (the other four are two sets of two that are meant to be read to other objects). However, I can only get it to read one -- either the first or the second depending on how I manipulate the code.
Here's what's in my data file:
Mustang Sally
123.45
George Porge
11.99
J. D. Rockerfeller
56321.3
And here's the section of the code where I need to read said data:
void account::readAccount(ifstream &inFile)
{
while (getline(inFile, name))
{
inFile.ignore();
inFile >> savings;
}
}
The code above is only reading in the second line.
I think I'm having a phantom newline problem, which I can't seem to resolve, but I also feel that there's another problem on top of that, which I can't comprehend with my current level of experience regarding file streams.
The code above is only reading in the second line.
Yes because you tell it to ignore. I don't know what exactly two lines you want to get from these, but based on the codes, I'm assuming that you want to read the values at line 2,4. The following code will print out those two lines.
float savings = 0.0f;
while(getline(inFile,line))
{
if(savings > 0.0f) cout << savings << endl;
inFile >> savings;
inFile.ignore(1000, '\n' );
}

Stringstream not reading as wanted

After having tried for a few hours to find out why my C++ code doesn't work as required, I've found out the error should be hiding within this piece of code:
void loadWorld(GameOfLife& game, int rij, int kolom, string fileName){
// Reads a .LIF-file and configures the GameOfLife object based on this info
ifstream ifs(fileName.c_str());
stringstream ls;
if(!ifs) throw new fileNotFound;
string line;
char tempChar;
Block tempBlock;
vector<Cel> tempVector(0);
string rule;
while(ifs.good()){
getline(ifs, line); //get next line from the file
ls.str(line); //put it into a stringstream
if(line!="") { //skip empty strings
ls >> tempChar;
if(tempChar=='#')
ls >> tempChar; //go to the next sign
switch(tempChar){
case 'N':
rule = "23/3"; //default rule
break;
case 'R':
ls >> rule; //set new rule
break;
case 'P' :
if(tempBlock.cellen.size()>0)
loadBlock(game, rij, kolom, tempBlock); //load previous block
//new block
tempBlock.cellen.clear();
ls >> tempBlock.x >> tempBlock.y;
break;
case '.' : case '*' :
cout << tempChar; //for testing
tempVector.clear();
if(tempChar=='.')
tempVector.push_back(Cel(0, fl_rgb_color(0,0,0)));
else
tempVector.push_back(Cel(1, fl_rgb_color(0,0,0)));
while(ls.good()){
ls >> tempChar;
cout << tempChar; //test
if(tempChar=='.')
tempVector.push_back(Cel(0, fl_rgb_color(0,0,0)));
else
tempVector.push_back(Cel(1, fl_rgb_color(0,0,0)));
}
tempBlock.cellen.push_back(tempVector);
cout << endl; //test
break;
default:
break;
}
}
}
loadBlock(game, rij, kolom, tempBlock); //load last block
int survival=0;
int birth=0;
extractRule(rule, survival, birth);
game.setSurvival(survival);
game.setBirth(birth);
}
The code is part of an implementation of Conway's Game Of Life and is supposed to read a file that contains information about a certain configuration, then configure the object 'game' of the type GameOfLife to contain this configuration. An example of a file that should be read is:
#Life 1.05
#D Acorn
#D The most vigorously growing 7-cell
#D "methuselah" pattern. See also RABBITS.
#N
#P -3 -1
.*
...*
**..***
The program should ignore the first four rules and upon reading the fifth rule, should set the rule of game to 23/3, the normal rule. It does all that.
It should also read blocks of code like the one following #P. For some reason, it does not do so. As you can see, I use cout as a debugging tool in the parts of the code that do not work as expected. My expected output would be:
.*
...*
**..***
but it is:
.**
*
*
I have no idea why this is the case. Please let me know if you have any tips on how to find the error. If you need more information (like code for used structures like Cel of Block), please let me know. I didn't include those as I suspected they would distract from the problem; it persists even when excluding the parts that use Block or Cel.
Note: The necessary includes have been made and the program compiles in Eclipse without any errors or warnings.
In addition to ls.str(line); for each line, you need the following line to clear error flags of ls.
ls.clear();
An even simpler way may be construct the stringstream after a line is read and destruct it once the line is done.
while(getline(ifs, line)) {
istringstream ls(line);
// ...
}
Two problems:
ls.good() is still true after you read the last character of the buffer. It's when you attempt to read again that it becomes false. Thus, you should check it after your attempted read from ls to ensure that you actually read in a character.
ls.str() does not clear the error flags, which means that all subsequent reads will fail. You'll need to call ls.clear() after that, or simply construct a new stringstream for each line.

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.

C++: Why does space always terminate a string when read?

Using type std::string to accept a sentence, for practice (I haven't worked with strings in C++ much) I'm checking if a character is a vowel or not. I got this:
for(i = 0; i <= analyse.length(); i++) {
if(analyse[i] == 'a' || analyse[i] == 'e' [..etc..]) {
...vowels++;
} else { ...
...consonants++;
}
This works fine if the string is all one word, but the second I add a space (IE: aeio aatest) it will only count the first block and count the space as a consonant, and quit reading the sentence (exiting the for loop or something).
Does a space count as no character == null? Or some oddity with std::string?, It would be helpful to know why that is happening!
EDIT:
I'm simply accepting the string through std::cin, such as:
std::string analyse = "";
std::cin >> analyse;
I'd guess you're reading your string with something like your_stream >> your_string;. Operator >> for strings is defined to work (about) the same as scanf's %s conversion, which reads up until it encounters whitespace -- therefore, operator>> does the same.
You can read an entire line of input instead with std::getline. You might also want to look at an answer I posted to a previous question (provides some alternatives to std::getline).
I can't tell from the code that you have pasted, but I'm going to go out on a limb and guess that you're reading into the string using the stream extraction operator (stream >> string).
The stream extraction operator stops when it encounters whitespace.
If this isn't what's going on, can you show us how you're populating your string, and what its contents are?
If I'm right, then you're going to want a different method of reading content into the string. std::getline() is probably the easiest method of reading from a file. It stops at newlines instead of at whitespace.
Edit based on edited question:
use this (doublecheck the syntax. I'm not in front of my compiler.):
std::getline(std::cin, analyze);
This ought to stop reading when you press "enter".
If you want to read in an entire line (including the blanks) then you should read using getline. Schematically it looks like this:
#include <string>
istream& std::getline( istream& is, string& s );
To read the whole line you do something like this:
string s;
getline( cin, s );
cout << "You entered " << s << endl;
PS: the word is "consonant", not "consenent".
The >> operator on an istream separates strings on whitespace. If you want to get a whole line, you can use readline(cin,destination_string).