Writing Single-Line Parsers in C++: Niels Joubert, CS184 TA, UC Berkeley 2008-09-30
Writing Single-Line Parsers in C++: Niels Joubert, CS184 TA, UC Berkeley 2008-09-30
0.1
Silicon Graphics did not only contribute fantastic advances in computer graphics to the developer community,
they also wrote a large bundle of C++ classes that became the equivalent of Javas built-in API for C++.
We will use the ifstream class to parse an input file into stringstream objects. Notice the abundance of
the stream class subclasses.
We use streams since they have two very useful operators defined on them:
operator>>
operator<<
By default, the >> extract operator considers whitespace to be a separator, and will return something of the
type of variable it is used on. In other words, you can do the following:
// i n s t r e a m c o n t a i n s a s t r i n g o f t h e f o r m a t 1 . 0 1 . 0 1 . 0 1 2 3
void parseCommand ( stream & i n s t r e a m ) {}
double x , y , z ;
int i , j , k ;
i n s t r e a m >> x >> y >> z >> i >> j >> k ;
}
The above will populate x, y and z with the correct double values of 1.0 and i, j and k with the expected
1, 2 and 3 respectively. The opposite is also valid - using << to pipe out different data types to file.
0.2
The obj file format defines geometry on a line-by-line basis of commands. Each line consists of an operator
followed by one or more operands. The subset of the obj file format that is very applicable to the raytracer
project is as follows:
FILE STRUCTURE:
[operator] [operands]\n
LINE STRUCTURE:
[operator]
[operands]
v
x y z
f
i j k
1 This
2 See
document does not describe the only way to write parsers, and not even the best way, but it is easy!
http://local.wasp.uwa.edu.au/ pbourke/dataformats/obj/ for a full discussion of the OBJ format.
(x, y, z) gives the world-space coordinates of a vertex. i, j and k gives the indices of three vertices that
make up a triangle, where indices are counted from 1 to n where 1 is the first vertex read in from the file
and n is the last. You can easily extend this format to support everything your raytracer does.
0.3
Using ifstream
ifstream allows us to represent a file on disk as a stream. Thus we can do the following (Please note the ...
are to indicate that you can (and probably will) be passing around other arguments as well.):
Listing 1: Using ifstream
void p a r s e S c e n e ( s t r i n g f i l e n a m e , . . . ) {
char l i n e [ 1 0 2 4 ] ; //We c r e a t e some temporary s t o r a g e
i f s t r e a m i n F i l e ( f i l e n a m e . c s t r ( ) , i f s t r e a m : : i n ) ; //Open as stream
if (! inFile ) {
c o u t << Could not open g i v e n f i l e << f i l e n a m e ;
exit (1);
}
while ( i n F i l e . good ( ) ) {
inFile . getline ( line , 1023);
//Read l i n e i n t o temporary s t o r a g e
i f ( ! parseLine ( string ( l i n e ) , . . . ))
//Do s o m e t h i n g w i t h l i n e
exit (1);
//An e r r o r o c c u r r e d ?
}
inFile . close ();
}
0.4
Using stringstream
We could run some regex expression on each line, but regex is much uglier than streams, so we can define
each line as a stream, and parse that line with the operators explained in section 0.1.
Listing 2: Using stringstream
bool p a r s e L i n e ( s t r i n g l i n e , . . . ) {
s t r i n g operator ;
i f ( l i n e . empty ( ) )
return true ;
s t r i n g s t r e a m s s ( s t r i n g s t r e a m : : i n | s t r i n g s t r e a m : : out ) ;
ss . str ( line );
s s >> operator ;
i f ( operator [ 0 ] == # ) { // a c c e s s s t r i n g s as a r r a y s .
return true ;
} e l s e i f ( operator . compare ( v ) == 0 ) {
double x , y , z ;
s s >>x >>y >>z ; //Now you have an x , y , z as d o u b l e s .
...
}
if ( ss . f a i l ())
return f a l s e ;
return true ;
}