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

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.

Related

Map enum key and value using function pointer in C++

i am very new to C++ programming. I am doing my master and working on one problem given by professor. the problem is regarding performing the basic operations on binary search tree. i have one file which has below format :
I 1015291402
I 729831403
I 1005116371
F 757970570
D 1005116371
F 729831403
I 1218751282
D 1015291402
I 582339464
I 92421221
There are three basic operations Insert, Delete and Find can be done on search tree. so i need to read this file perform operation line by line. Below is the code i wrote so far.
string line;
ifstream infilesmall("inputfile_small.txt");
//ifstream infilelarge("inputfile_small.txt");
while (getline(infilesmall, line))
{
istringstream iss(line);
vector<string> tokens;
copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter(tokens));
string str = ((tokens)._Myfirst)[0];
cout<< ((tokens)._Myfirst)[1];
//char operation = new char(((tokens)._Myfirst)[0]);
/*typedef void (*funcPointer)(int);
void String1Action(int arg);
void String2Action(int arg);
map<string, funcPointer> stringFunctionMap;
stringFunctionMap.add("string1", &String1Action);*/
insert(t,10);
find(t,0);
//Delete(t,10);
}
so the question what is ideal way of calling Insert, Delete and Find by splitting the line? i need to take care the performance. One approach i found out is to create enum with key and value pair and having function pointer. so depending on the key value ("I","D","F") the appropriate function will be called with it's respective value. Can you please suggest me/correct my approach and guide me on this code. Appreciate your time. Thanks
Your code is needlessly complex. You can read the operator and the number from the file, one pair at a time, and use the number appropriately based the value of the operator.
char op;
int number;
while ( infilesmall >> op >> number )
{
switch (op)
{
case 'I':
insertData(number);
break;
case 'D':
deleteData(number);
break;
case 'F':
findData(number);
break;
default:
std::err << "Unknown operator, " << op << std::endl;
}
}

outputting from a file stuck in loop and not reading?

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

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.

Proper character by character input?

To better familiarize myself with C++, I'm redoing an old college OS assignment: program your own shell. The first thing I tackled was accepting commands but my approach leaves some features to be desired. Here's what I have:
string GetLine() {
string line;
char input = _getch();
while (input != 13) {
switch (input) {
case 8: // backspace
if (line.length() != 0) {
line = line.substr(0, line.length() - 1);
cout << "\b \b";
}
break;
case -32: // all arrows' first byte
input = _getch(); // distinctly different arrow byte
switch (input) {
case 72: // up
break;
case 80: // down
break;
case 75: // left
break;
case 77: // right
break;
}
break;
case 9: // tab
break;
default:
line += input;
cout << input;
break;
}
input = _getch();
}
cout << endl;
return line;
}
It works pretty well and I know I have more to do with the arrow keys (if I'm even distinguishing between them correctly as is). The main problem I'm asking about is a certain situation where the cursor has wrapped to the next line on the terminal. Aiming to emulate cmd.exe I print a prompt and wait at the end of it for the user's input. When this prompt is long, it only takes a few characters before my command runs across two lines. If I just type it out and let it wrap around and press enter everything works fine. If I wrap to the second line and want to backspace back to the first line, the cursor stops at the first character on it's new line and won't "unwrap" back to the first line as I'd hope. What can I do here to achieve this unwrapping behaviour?
I think the problem is that trying to print '\b' won't unwrap, but I don't have an idea for an alternative and I am hoping that I don't have to treat this as a special case and that code that will backspace in the middle of a line will also work at the beginning of a line.
You need to use the winapi, SetConsoleCursorPosition will help you. Here is a minimal example that moves the cursor up one row from the current position:
#include <iostream>
#include <windows.h>
int main() {
std::cout << "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaa";
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
csbiInfo.dwCursorPosition.Y -= 1; // Move Y coordinate up by one
SetConsoleCursorPosition(hStdout, csbiInfo.dwCursorPosition);
std::cin.get();
}
Also, Console Functions has an entire list of all the necessary functions to control the console. You will want to use these in your program.
You will need to make your own screen buffer with CreateConsoleScreenBuffer and then set it as the buffer first. Then after that handle everything written and read from the console yourself.

Loop for instream from file

I'm trying to get my program to loop through the file, taking in a load of information each time. However, at the moment after 2 correctly inputted lines it always goes to default, no matter what the contents of the file. Originally it was an eof while loop but I changed it to a for loop to try and fix it. Here's my code:
ifstream furniture;
furniture.open("h://furniture.txt");
for(int i=0;i<=count;i++)
{
type=0;
furniture>>type>>name>>number>>material>>colour>>mattress;
switch (type)
{
case 1:
{
Item* item= new Bed(number, name, material, colour, mattress);
cout<<"working, new bed"<<endl;
v.push_back(item);
cout<<"working pushback"<<endl;
count++;
break;
}
case 2:
{
Item* item= new Sofa(number, name, material, colour);
cout<<"working, new sofa"<<endl;
v.push_back (item);
cout<<"working pushback"<<endl;
count++;
break;
}
case 3:
{
Item* item= new Table(number, name, material, colour);
cout<<"working, new table"<<endl;
v.push_back(item);
cout<<"working pushback"<<endl;
count++;
break;
}
default:
{
cout<<"Invalid input"<<endl;
type=0;
break;
}
}
}
I have tried a series of different solutions but nothing seems to have solved the problem.
Any help would be greatly appreciated.
ifstream's operator>> does not change the variable if the read fails, so type remains 0, thus the default branch is called. It seems like your file has a formatting error that makes the read fail.
Based on your comment, that you might not have all values in the file all the time, here is what you could do:
The best way would be to use getline() to read a full line, then use stringstream to extract the values from the line, handling the optional values in the process:
string line;
getline(furniture, line);
stringstream ss(line);
ss>>type>>name>>number>>material>>colour>>mattress;
If you provide default values to all those variables, they will keep the default if the line is ill-formatted or does not contain all of them.
The advantage of using getline() here is that the extraction via op>> does not mess up subsequent lines if there is a problem with the current line.