Reading in a file.

Started by
13 comments, last by dreamslayerx 12 years, 7 months ago
Hi guys. I trying to do something very simple,but for some reason. When using inFile.ignore('\n'). I am not getting want I want to display. The goal here is to read in the v, vertex, vt, vertex texture, and f, faces so that I can apply some texturing later. I would like if if whenever I come across v, vt, or f, I would like to read in the values and ignoring all the other information. Does anyone have any idea? Keep it simple I am new.

You can read the .obj file using wordPad or someother text editor. I understand how to read in code, but maybe you guys know a more efficient way to extract this data.

[/file]
# Blender3D v249 OBJ File: cube.blend

# www.blender3d.org

mtllib cube.mtl

v 1.000000 -1.000000 -1.000000

v 1.000000 -1.000000 1.000000

v -1.000000 -1.000000 1.000000

v -1.000000 -1.000000 -1.000000

v 1.000000 1.000000 -1.000000

v 0.999999 1.000000 1.000001

v -1.000000 1.000000 1.000000

v -1.000000 1.000000 -1.000000

vt 0.666667 0.000000

vt 0.666667 0.333333

vt 0.333334 0.333333

vt 0.333333 0.000000

vt 0.333333 1.000000

vt 0.333334 0.666667

vt 0.666667 0.666667

vt 0.666667 1.000000

vt 0.333333 0.333333

vt 0.333333 0.666667

vt 0.000000 0.666667

vt 0.000000 1.000000

vt 0.000000 0.333333

vt 0.000000 0.000000

usemtl Material_cube_Cube.bmp

s off

f 5/1 1/2 4/3

f 5/1 4/3 8/4

f 3/5 7/6 8/7

f 3/5 8/7 4/8

f 2/2 6/7 3/9

f 6/7 7/6 3/9

f 1/10 5/5 2/11

f 5/5 6/12 2/11

f 5/9 8/13 6/4

f 8/13 7/14 6/4

f 1/10 2/11 3/13

f 1/10 3/13 4/9
[/file]

Advertisement
Show your code.

Show your code.



Oh sure it is your basic code,but I have been playing around with it a little, but it looks like this. I'm really asking for tips. Many things are commented out because I have been trying different things. I would like to know if someone has an effiecient method for reading this.

[color="#0000ff"][color="#0000ff"]void readFile() [color="#008000"][color="#008000"]//Working section Used to count the number of floats in a file. This section can make program slower.

{

ifstream inFile;

inFile.open([color="#a31515"][color="#a31515"]"cube.obj");

[color="#0000ff"][color="#0000ff"]if (!inFile.is_open())

{

cout << [color="#a31515"][color="#a31515"]"Unable to open file"; [color="#008000"][color="#008000"]///C:\Users\Alanzo Granville\Documents\Visual Studio 2008\Projects\Opening_A_File_Program\Opening_A_File_Program

exit(1); [color="#008000"][color="#008000"]/// terminate with error

}

[color="#008000"][color="#008000"]// inFile.peek() This will be the section where I peek into the file to see what is going to be read next;

[color="#0000ff"][color="#0000ff"]int i =0;

[color="#0000ff"][color="#0000ff"]char ch;

[color="#0000ff"][color="#0000ff"]int num;

[color="#008000"][color="#008000"]//Reading the first character of the file

//inFile>>ch;

//cout<<ch<<' ';

//ch = inFile.peek();

inFile>>ch;

[color="#008000"][color="#008000"]//inFile.ignore(25,'\n');

[color="#0000ff"][color="#0000ff"]while(!inFile.eof())

{

[color="#008000"][color="#008000"]//ch = inFile.peek();

[color="#008000"][color="#008000"]//inFile>>ch;

[color="#008000"][color="#008000"]//cout<<ch<<endl;

[color="#008000"][color="#008000"]//char ch;

//ch = inFile.peek();

[color="#008000"][color="#008000"]//if(ch == '#')

[color="#008000"][color="#008000"]// {

[color="#008000"][color="#008000"]//if (i<3)

[color="#008000"][color="#008000"]//{

//inFile.ignore(50,'\n');

// }

// if(i==3)

// ch=inFile.peek();

//cout<<" You are inside the first if statment"<<endl;



[color="#0000ff"][color="#0000ff"]if(ch == [color="#a31515"][color="#a31515"]'v' || [color="#a31515"][color="#a31515"]'t'||[color="#a31515"][color="#a31515"]'f'||[color="#a31515"][color="#a31515"]'/')

{

inFile>>ch;

[color="#0000ff"][color="#0000ff"]if(ch == [color="#a31515"][color="#a31515"]'0'||ch ==[color="#a31515"][color="#a31515"]'1'||ch ==[color="#a31515"][color="#a31515"]'2'||ch ==[color="#a31515"][color="#a31515"]'3'||ch ==[color="#a31515"][color="#a31515"]'4'||ch ==[color="#a31515"][color="#a31515"]'5'||ch ==[color="#a31515"][color="#a31515"]'6'||ch ==[color="#a31515"][color="#a31515"]'7'||

ch ==[color="#a31515"][color="#a31515"]'8'||ch ==[color="#a31515"][color="#a31515"]'9'||ch ==[color="#a31515"][color="#a31515"]'.'||ch ==[color="#a31515"][color="#a31515"]'-')

{

[color="#008000"][color="#008000"]//cout<<"Did you get in"<<endl;

[color="#008000"][color="#008000"]//inFile >> ch;// num;

[color="#008000"][color="#008000"]//inFile>>ch;



cout<<ch<<[color="#a31515"][color="#a31515"]' ';

i++;

[color="#008000"][color="#008000"]//inFile>>ch;

[color="#008000"][color="#008000"]//cout << ch<<' '<<endl;

}

[color="#008000"][color="#008000"]//else

}



[color="#008000"][color="#008000"]//inFile.get();

[color="#008000"][color="#008000"]//}

[color="#008000"][color="#008000"]//inFile.get();

//floatC=i;

}[color="#008000"][color="#008000"]//end of while loop

}

No need to extract every byte like that unless you are writing a compiler or something.

You should be able to read and parse this file in less than 10 lines using idiomatic C++

I suggest you look into the global getline function. This function can be used to read the file line by line.

Each line (that isn't empty) can be fed to a [url="http://www.cplusplus.com/reference/iostream/stringstream/"]stringstream[/url] object. This object will let you split a line into items, and convert them at the same time.
Just what you need.

Basically you extract the first item from the stringstream as a string, if that string is a 'v', you extract the next three items into some suitable variables, like floats. Conversion is implicit.

example

No need to extract every byte like that unless you are writing a compiler or something.

You should be able to read and parse this file in less than 10 lines using idiomatic C++

I suggest you look into the global getline function. This function can be used to read the file line by line.

Each line (that isn't empty) can be fed to a stringstream object. This object will let you split a line into items, and convert them at the same time.
Just what you need.

Basically you extract the first item from the stringstream as a string, if that string is a 'v', you extract the next three items into some suitable variables, like floats. Conversion is implicit.

example



Ok I follow what you said thanks, but I am having an issue with it reading in spaces into the arrays. Any Idea on how to stop that or ignore spaces. Below is how the code looks. I have commented out alot of things, so those things can be ignored.



[color="#0000ff"][color="#0000ff"]void readFile() [color="#008000"][color="#008000"]//Working section Used to count the number of floats in a file. This section can make program slower.

{

ifstream inFile;

inFile.open([color="#a31515"][color="#a31515"]"cube.obj");

[color="#0000ff"][color="#0000ff"]if (!inFile.is_open())

{

cout << [color="#a31515"][color="#a31515"]"Unable to open file"; [color="#008000"][color="#008000"]///C:\Users\Alanzo Granville\Documents\Visual Studio 2008\Projects\Opening_A_File_Program\Opening_A_File_Program

exit(1); [color="#008000"][color="#008000"]/// terminate with error

}

[color="#008000"][color="#008000"]// inFile.peek() This will be the section where I peek into the file to see what is going to be read next;

[color="#0000ff"][color="#0000ff"]int i =0;

[color="#008000"][color="#008000"]//here we want to read in 3 different types of values,

/* 1) Declare our stringstream as stringstream ss;

2) Use a getline command to to read the entire line

ex: inFile.getline(how many items to read, string object that extracted content is stored, stops when this char is read )

inFile.get(buff, bufferSize); //(We are using this one) This implies if buff is 6, 6 items will be read, and bufferSize will store those 6 items

into some char array

or

inFile.get(buff, bufferSize, ','); //Here is similar but the last char ',' tells the comman to stop reading when it reaches a ',' comma

4)Now we need to store what is read from getline into our string stream ss.

ex:

ss << buff;

//now a for loop or while loop can be written to make sure that we read all the data from ss



ss.getline(date read into ss, number of inputs, character to discard); //This states that

ex:

ss.getline(buff, 6, ','); //Here the data read in is buff and reads 6 times, and the delimitor or stop read point is the comma which

will be discarded. Reads 6 lines

5)Now convert input value that was read in from ss into the object that we want such as int, float, char, etc using atoi function from stdlib library

ex.

suppose the variable that we want to store it in is and array called store[rows][colums] wher rows is the number of rows and colums the number of columns

store[row][column] = atoi(buff); if buff is an int, now the string data will be changed into an int object, same goes for float or char

//atof(buff) changes the data into a float

6) Then before outter loop ends or while loop ends to start again input any character into ss to do the need formating if neccessary. Then clear ss.

ex.

ss<<" "; //puts space between each character read

// now to clear. Clearing ss allows for us to read the new data that is written

ex.

ss.clear();

and results can be read out by using store[row][column]

//We are going to manipulate

1) check to see if string is v, vt, or f;

2) create conditional statemes for each one for example:

example:



//Here we may have to make stringstream ss read until it reaches a space or getline read until it reaches a space

ex:

inFile.getline(float data type, ' '); //I may have to play around with this to get it working properly. Reads till it reachs a space

if (string == 'v')

{





}*/

[color="#0000ff"][color="#0000ff"]char ch; [color="#008000"][color="#008000"]//buffer size

[color="#008000"][color="#008000"]//I need to find out why my arrays cann't take other variables

[color="#0000ff"][color="#0000ff"]int gather=1000; [color="#008000"][color="#008000"]//buffer size

[color="#0000ff"][color="#0000ff"]char buff[1000]; [color="#008000"][color="#008000"]//stating that the size of the buff array is equal to gather

[color="#0000ff"][color="#0000ff"]float vertPoint[1000][5]; [color="#008000"][color="#008000"]//for verteices until I pass data to structs has to be global

[color="#0000ff"][color="#0000ff"]float texPoint[1000][3];

[color="#0000ff"][color="#0000ff"]int j=0;

[color="#008000"][color="#008000"]//Reading the first character of the file

[color="#0000ff"][color="#0000ff"]while(!inFile.eof())

{

inFile>>ch;

[color="#008000"][color="#008000"]//cout<<ch<<endl;

[color="#0000ff"][color="#0000ff"]if(ch == [color="#a31515"][color="#a31515"]'v')

{

[color="#008000"][color="#008000"]//cout<<"You made it into the first if statment"<<endl;

inFile.getline(buff,[color="#a31515"][color="#a31515"]' '); [color="#008000"][color="#008000"]//right now this sets our buff array variabler

cout<<[color="#a31515"][color="#a31515"]"This is the buff array "<<buff<<endl;

[color="#0000ff"][color="#0000ff"]if( buff[0] == [color="#a31515"][color="#a31515"]' ') [color="#008000"][color="#008000"]//checks the first character read in by buff to see if we have a vertex, vertex texture, or triangle face

{

[color="#008000"][color="#008000"]//cout<<"You made it into the second if statement"<<endl;

ss<<buff; [color="#008000"][color="#008000"]//This reads the data into the stringstream ss



vertPoint[j] = atof(buff); [color="#008000"][color="#008000"]//stringstream data that is stored in ss is given to variable point as a floating point value

cout<<vertPoint[j]<<endl; [color="#008000"][color="#008000"]//line used to makes sure that I have proper output; It can be commented out

j++;

i=(i+1)%5; [color="#008000"][color="#008000"]//This will keep the i value between 0 to 2, while j increases with the number of vertices since it is

[color="#008000"][color="#008000"]//storing spaces into the array



}

[color="#008000"][color="#008000"]//I'll fix this part after I finish with the above section with storing properly in an array

[color="#0000ff"][color="#0000ff"]if( buff[0] == [color="#a31515"][color="#a31515"]'t') [color="#008000"][color="#008000"]//checks the first character read in by buff to see if we have a vertex, vertex texture, or triangle face

{

[color="#0000ff"][color="#0000ff"]if(buff[0] = [color="#a31515"][color="#a31515"]' ');

{

[color="#008000"][color="#008000"]//I need to ignore the spaces readin by buff



ss<<buff; [color="#008000"][color="#008000"]//This reads the data into the stringstream ss

[color="#008000"][color="#008000"]//Its not that it is reading spaces arrays are incremented like so i=1, j=1, then i=2, j=2



texPoint[j]= atof(buff); [color="#008000"][color="#008000"]//stringstream data that is stored in ss is given to variable point as a floating point value

[color="#008000"][color="#008000"]//cout<<texPoint[j]<<endl; //line used to makes sure that I have proper output; It can be commented out

[color="#008000"][color="#008000"]//j++;

[color="#008000"][color="#008000"]//i=(i+1)%3;



}

}

}





}

[color="#008000"][color="#008000"]/*for(i =0; i<2;i++)

{

for(j=0; j<14;j++)

{

cout<<texPoint[j]<<endl;

}

}*/

ss.getline(buff,[color="#a31515"][color="#a31515"]'\n');

cout<<ss.str()<<endl;; [color="#008000"][color="#008000"]//allows me to see the entire string thats read in ouput to the screen

cout<<[color="#a31515"][color="#a31515"]"Test Number "<<vertPoint[0][0]<<[color="#a31515"][color="#a31515"]' '<<vertPoint[2][2]<<[color="#a31515"][color="#a31515"]' '<< vertPoint[3][3]<<endl; [color="#008000"][color="#008000"]//This is how my output is set up reads first line correctly

ss.clear(); [color="#008000"][color="#008000"]//clears the stringstream ss

//inFile.getline(buff,2); //This read the entire line up to 1000 characters; it stops at the end of the line

[color="#008000"][color="#008000"]//unless the line contains more than 1000 characters.

//while(!inFile.eof())//for (int i =0; i<3; i++)

//{

/*if(buff == " #")

{

ss << buff; //This stores

ss.getline(buff,1,' ');

ss<<' ';

cout<< ss.str();//<<endl; //This prints out the data read into ss from buff.

ss.clear();

}*/

//}//end of while loop

//while(!inFile.eof())

// {



[color="#008000"][color="#008000"]//}//end of while loop

}





[/output]
//output without all the wording
//just comment out the line of code where it says cout<<[color="#a31515"][color="#a31515"]"This is the buff array "<<buff<<endl;




1
1
-1
-1
1
0.999999
-1
-1
1.000000 -1.000000 -1.000000 1.000000 -1.000000 1.000000 -1.000000 -1.000000 1.
000000 -1.000000 -1.000000 -1.000000 1.000000 1.000000 -1.000000 0.999999 1.0000
00 1.000001 -1.000000 1.000000 1.000000 -1.000000 1.000000 -1.000000 0.666667 0
.000000 0.666667 0.333333 0.333334 0.333333 0.333333 0.000000 0.333333 1.000
000 0.333334 0.666667 0.666667 0.666667 0.666667 1.000000 0.333333 0.333333
0.333333 0.666667 0.000000 0.666667 0.000000 1.000000 0.000000 0.333333 0.0
00000 0.000000
Test Number 1 -1 -1

Press any key to continue . . .

[/output]


Ok I follow what you said thanks, but I am having an issue with it reading in spaces into the arrays. Any Idea on how to stop that or ignore spaces. Below is how the code looks. I have commented out alot of things, so those things can be ignored.


If you're just getting stuck on parsing a delimited string, a quick google search turns up lots of results, such as
* http://www.cplusplus.com/forum/general/17771/
and if you want to use stringstream there's a few examples out there...

* http://www.dreamincode.net/forums/topic/95826-stringstream-tutorial/

Also, maybe it's just the formatting of your post, but a big function like that might only serve to confuse the matter more, especially when you come back to it later. Once you have a parsing function the process of reading the file should be very trivial and as pulpfist suggests, only take a few lines of code.




Here is a small example of how you could get started parsing the file.
I have to admit, its going to be more than 10 lines lol.
Note that this example is far from finished.
[source lang="cpp"]
void readFile()
{
ifstream inFile("cube.obj"); // Create and open file
if (!inFile.is_open())
{
cout << "Unable to open file" << endl;
exit(1);
}

string line, item, lib;
float x, y, z;

while(getline(inFile, line)) // Continue to read the next line until the end of file
{
stringstream ss(line); // Create a new stringstream and fill it with the line
if(!(ss >> item)) // Extract the first item. If there is no items left, this expression will be false and we continue to read the next line.
continue;

if(item == "#")
continue; // We found a comment. Continue to read the next line.
else if(item == "v")
{
ss >> x >> y >> z; // Extract and convert the next three items from the stream
cout << "We found a v: " << x << " " << y << " " << z << endl;
}
else if(item == "vt")
{
ss >> x >> y; // Extract and convert the next two items from the stream
cout << "We found a vt: " << x << " " << y << endl;
}
else if(item == "mtllib")
{
ss >> lib; // Extract the next item from the stream
cout << "We found a mtllib: " << lib << endl;
}
// And so on...
}
}[/source]

Since an expression like this ss >> x >> y >> z; returns false if there is less that three items left in the stream, we can take advantage of this to make the function a bit more robust:
if(!(ss >> x >> y >> z)) // report error...

Also note that the expression else if(item == "vt") is case sensitive, so it won't match a VT or a Vt.
Don't keep a bunch of outdated comments in your file. Instead, use version control. This way you'll feel free to add or remove code as you like, you can always get it back the way you had it later.

The sheer number of comments is hiding things from you. For example, this line:

if(buff[0] = ' ');
{
// code...
}

Note the semicolon after the if statement. This is treated as the following:

if(buff[0] = ' ')
{
// Nothing to do here!
}

// code...

That is, your code is unconditionally executed.

Using fixed size buffers like you are doing is a recipe for failure - someone is either going to have a data file with lines longer than you expect, or more vertices than you catered for.

Using atof() makes it hard for you to test for failure - on failure it returns 0, which is a perfectly valid float!

Instead, use std::string and std::vector:


struct Vertex
{
float x;
float y;
float z;
};

struct TexPoint
{
float u;
float v;
};

// TODO: more sophisticated error reporting (e.g. build a vector of struct Error { int line, SomeErrorEnumeration errorType; })
bool parseLine(const std::string &line, std::vector<Vertex> &vertices, std::vector<TexPoint> &texturePoints)
{
if(line.empty())
{
return true;
}

std::stringstream stream(line);

char c;
if(!stream >> c)
{
return false;
}

if(c == '#')
{
return true;
}
else if(c == 'v')
{
if(stream.peek() == 't')
{
TexPoint texture;
if((stream >> texture.u >> texture.v) && (stream >> std::ws) && stream.eof())
{
texturePoints.push_back(texture);
return true;
}
// Failed to parse floats, or unexpected garbage at end of line
return false;
}
else
{
Vertex vertex;
if((stream >> vertex.x >> vertex.y >> vertex.z) && (stream >> std::ws) && stream.eof())
{
vertices.push_back(vertex);
return true;
}
// Failed to parse floats, or unexpected garbage at end of line
return false;
}
}
// Unhandled character
return false;
}

bool readFile(std::vector<Vertex> &vertices, std::vector<TexPoint> &texturePoints)
{
std::ifstream stream("cube.obj");

if(!stream.is_open())
{
// TODO: better error notification
cout << "Unable to open file";
return false;
}

std::string line;
while(std::getline(stream, line))
{
bool b = parseLine(line, vertices, texturePoints);
// TODO: test b...
}
return true;
}

Something like that (it isn't tested).
Just for future reference, from your first code, this doesn't work the way you think:


if(ch == 'v' || 't'||'f'||'/')


In english, it means: if ch equals v or if t isn't zero or if f isn't zero or if / isn't zero. Since none of the latter three are zero, the condition will always be true.


if(ch == 'v' || ch == 't' || ch == 'f' || ch == '/')


would be correct.


else if(c == 'v')
{
if(stream.peek() == 't')
{
TexPoint texture;
if((stream >> texture.u >> texture.v) && (stream >> std::ws) && stream.eof())


Sorry if having a senior moment, ripoff, but would you not need to consume that 't' after the peek before reading the first number?
Yup, you would. As I said, untested. One could use pulpfist's suggestion of reading the first token into a string rather than peeking.

This topic is closed to new replies.

Advertisement