Reading From Files in c++

M

Michael

Hi,
I moved to c++ from c, and wanted to know what the best way to read data
from files is in c++. Any thoughts? fscanf() is possible but fairly painful!

Regards

Michael
 
M

Michael

so for example reading in the file...

without many
fscanf(fp,"%s",variableAddr);

it just looks very messy!

TIA

Michael.



//File Start:

*3DSMAX_ASCIIEXPORT 200
*COMMENT "AsciiExport Version 2.00 - Tue Apr 06 19:00:45 2004"
*SCENE {
*SCENE_FILENAME "City_vertex_paint.max"
*SCENE_FIRSTFRAME 0
*SCENE_LASTFRAME 500
*SCENE_FRAMESPEED 30
*SCENE_TICKSPERFRAME 160
*SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000
*SCENE_AMBIENT_STATIC 0.9804 0.9804 0.9804
}
*MATERIAL_LIST {
*MATERIAL_COUNT 13
*MATERIAL 0 {
*MATERIAL_NAME "SF Building"
*MATERIAL_CLASS "Multi/Sub-Object"
*MATERIAL_AMBIENT 0.1000 0.1000 0.1000
*MATERIAL_DIFFUSE 0.0392 0.0392 0.0392
*MATERIAL_SPECULAR 0.9000 0.9000 0.9000
*MATERIAL_SHINE 0.2500
*MATERIAL_SHINESTRENGTH 0.0500
*MATERIAL_TRANSPARENCY 0.0000
*MATERIAL_WIRESIZE 1.0000
*NUMSUBMTLS 6
*SUBMATERIAL 0 {
*MATERIAL_NAME "Top"
*MATERIAL_CLASS "Standard"
*MATERIAL_AMBIENT 0.1000 0.1000 0.1000
*MATERIAL_DIFFUSE 0.0392 0.0392 0.0392
*MATERIAL_SPECULAR 0.9000 0.9000 0.9000
*MATERIAL_SHINE 0.2500
*MATERIAL_SHINESTRENGTH 0.0500
*MATERIAL_TRANSPARENCY 0.0000
*MATERIAL_WIRESIZE 1.0000
*MATERIAL_SHADING Blinn
*MATERIAL_XP_FALLOFF 0.0000
 
P

Petec

Michael said:
so for example reading in the file...

without many
fscanf(fp,"%s",variableAddr);

it just looks very messy!

TIA

Michael.

<snip>

Check out iostreams, especially std::ifstream.
They can be used just like std::cout.

- Pete
 
M

Mike Wahler

Michael said:
so for example reading in the file...

without many
fscanf(fp,"%s",variableAddr);

it just looks very messy!

TIA

Michael.



//File Start:

*3DSMAX_ASCIIEXPORT 200
*COMMENT "AsciiExport Version 2.00 - Tue Apr 06 19:00:45 2004"
*SCENE {
*SCENE_FILENAME "City_vertex_paint.max"
*SCENE_FIRSTFRAME 0
*SCENE_LASTFRAME 500
*SCENE_FRAMESPEED 30
*SCENE_TICKSPERFRAME 160
*SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000
*SCENE_AMBIENT_STATIC 0.9804 0.9804 0.9804
}
*MATERIAL_LIST {
*MATERIAL_COUNT 13
*MATERIAL 0 {
*MATERIAL_NAME "SF Building"
*MATERIAL_CLASS "Multi/Sub-Object"
*MATERIAL_AMBIENT 0.1000 0.1000 0.1000
*MATERIAL_DIFFUSE 0.0392 0.0392 0.0392
*MATERIAL_SPECULAR 0.9000 0.9000 0.9000
*MATERIAL_SHINE 0.2500
*MATERIAL_SHINESTRENGTH 0.0500
*MATERIAL_TRANSPARENCY 0.0000
*MATERIAL_WIRESIZE 1.0000
*NUMSUBMTLS 6
*SUBMATERIAL 0 {
*MATERIAL_NAME "Top"
*MATERIAL_CLASS "Standard"
*MATERIAL_AMBIENT 0.1000 0.1000 0.1000
*MATERIAL_DIFFUSE 0.0392 0.0392 0.0392
*MATERIAL_SPECULAR 0.9000 0.9000 0.9000
*MATERIAL_SHINE 0.2500
*MATERIAL_SHINESTRENGTH 0.0500
*MATERIAL_TRANSPARENCY 0.0000
*MATERIAL_WIRESIZE 1.0000
*MATERIAL_SHADING Blinn
*MATERIAL_XP_FALLOFF 0.0000

#include <algorithm>
using std::copy;

#include <cstdlib>

#include <fstream>
using std::ifstream;

#include <iostream>
using std::cerr;
using std::cout;

#include <iterator>
using std::back_inserter;
using std::istream_iterator;
using std::eek:stream_iterator;

#include <string>
using std::string;

#include <vector>
using std::vector;


int main()
{
const int status[] = {EXIT_SUCCESS, EXIT_FAILURE};

std::string filename("data.txt");
ifstream input(filename.c_str());
bool err(!input);

if(!err)
{
vector<string> data;

copy(istream_iterator<string>(input),
istream_iterator<string>(),
back_inserter(data));

if(err = (!input && !input.eof()))
cerr << "Error reading input file " << filename << '\n';

cout << "Data read:\n";

copy(data.begin(), data.end(),
ostream_iterator<string>(cout, "\n"));
}
else
cerr << "Cannot open input file " << filename << '\n';

return status[err];
}


-Mike
 
K

Kevin Goodsell

Michael said:
so for example reading in the file...

without many
fscanf(fp,"%s",variableAddr);

You should never use *any* of these! This is a terrible bug, and a
likely security hole. Never, never use %s or %[ without a (correct)
field width. There's nothing to prevent someone from overflowing the
buffer, in which case the best you can hope for is a program crash. The
comp.lang.c FAQ has some good information on this, and reasons why you
should avoid the scanf() functions even in C. In C++ there's usually no
reason to even consider scanf(), printf(), and friends. The stream
classes are more flexible and much safer.

However, the problem with the code above is (unfortunately) also easy to
introduce when using streams:

char buff[some_size];
std::cin >> buff; // SERIOUS ERROR!

Again, there's nothing to prevent the buffer from being overflowed. Such
an overflow can have all kind of terrible consequences -- in particular,
it can often be exploited by a malicious person (or program) to gain
control of the system. Problems similar to this are probably the single
biggest cause of security holes that allow worms to infect systems.

This is why "strings" should never be represented as character arrays,
unless you know what you are doing and are very, very careful. In C++,
use std::string instead:

std::string buff;
std::cin >> buff; // Go ahead, TRY to overflow it!

-Kevin
 
M

Michael

Hi,
I have tried to implement it but get a problem, thuis is my code at the mo:
(Header includes removed for clarity)

{
string str
char junk[100];
int MaxLineLength =100;
const int status[] = {EXIT_SUCCESS, EXIT_FAILURE};
ifstream input(FileName.c_str());
bool err(!input);

input >> str;
/* Step to Here: str is *3DSMAX_ASCIIEXPORT */
input >> str;
/* Step to Here: str is ??? */
input >> str;
}



start of Source File:
*3DSMAX_ASCIIEXPORT 200
*COMMENT "AsciiExport Version 2.00 - Tue Apr 06 19:00:45 2004"
*SCENE {
*SCENE_FILENAME "City_vertex_paint.max"
*SCENE_FIRSTFRAME 0
*SCENE_LASTFRAME 500
*SCENE_FRAMESPEED 30
*SCENE_TICKSPERFRAME 160
*SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000
*SCENE_AMBIENT_STATIC 0.9804 0.9804 0.9804
}
*MATERIAL_LIST {
*MATERIAL_COUNT 13
*MATERIAL 0 {
*MATERIAL_NAME "SF Building"
*MATERIAL_CLASS "Multi/Sub-Object"
*MATERIAL_AMBIENT 0.1000 0.1000 0.1000
*MATERIAL_DIFFUSE 0.0392 0.0392 0.0392
*MATERIAL_SPECULAR 0.9000 0.9000 0.9000
*MATERIAL_SHINE 0.2500
*MATERIAL_SHINESTRENGTH 0.0500
*MATERIAL_TRANSPARENCY 0.0000
*MATERIAL_WIRESIZE 1.0000
*NUMSUBMTLS 6
*SUBMATERIAL 0 {
*MATERIAL_NAME "Top"
*MATERIAL_CLASS "Standard"
*MATERIAL_AMBIENT 0.1000 0.1000 0.1000
*MATERIAL_DIFFUSE 0.0392 0.0392 0.0392
*MATERIAL_SPECULAR 0.9000 0.9000 0.9000
*MATERIAL_SHINE 0.2500
*MATERIAL_SHINESTRENGTH 0.0500
*MATERIAL_TRANSPARENCY 0.0000
*MATERIAL_WIRESIZE 1.0000
*MATERIAL_SHADING Blinn
*MATERIAL_XP_FALLOFF 0.0000
*MATERIAL_SELFILLUM 0.0000
*MATERIAL_FALLOFF In
*MATERIAL_XP_TYPE Filter
}
*SUBMATERIAL 1 {
*MATERIAL_NAME "Bottom"
*MATERIAL_CLASS "Standard"

Now the code compiles fine, but when I step through it in the debugger, the
first string reads: *3DSMAX_ASCIIEXPORT, but the next time MSVC++ debugger
gives its value as ???
What am i doing wrong?
Also what is the best way to read this into relevant structures, If i define
a class material for instance, can i overload the >> operator for it and
call that after the reading function had read "*MATERIAL 0"??
I'm not yet a great fan of streams :)

Thanks & Regards

Michael
 
K

Kevin Goodsell

Michael said:
Hi,
I have tried to implement it but get a problem, thuis is my code at the mo:
(Header includes removed for clarity)

Removing relevant code never adds clarity.
{
string str
char junk[100];
int MaxLineLength =100;
const int status[] = {EXIT_SUCCESS, EXIT_FAILURE};
ifstream input(FileName.c_str());
bool err(!input);

input >> str;
/* Step to Here: str is *3DSMAX_ASCIIEXPORT */
input >> str;
/* Step to Here: str is ??? */
input >> str;
}



start of Source File:
*3DSMAX_ASCIIEXPORT 200
*COMMENT "AsciiExport Version 2.00 - Tue Apr 06 19:00:45 2004"
*SCENE {
*SCENE_FILENAME "City_vertex_paint.max"
*SCENE_FIRSTFRAME 0

Now the code compiles fine, but when I step through it in the debugger, the
first string reads: *3DSMAX_ASCIIEXPORT, but the next time MSVC++ debugger
gives its value as ???

What about printing the string? Debuggers don't always show things
correctly.
What am i doing wrong?
Also what is the best way to read this into relevant structures, If i define
a class material for instance, can i overload the >> operator for it and
call that after the reading function had read "*MATERIAL 0"??

You can do that. >> is usually used for single input items, not
collections, so it's a bit unconventional, but it should work. You could
also just have a Read() member that does the same thing that the
operator >> would do.

-Kevin
 
M

Michael

Kevin Goodsell said:
Michael said:
Hi,
I have tried to implement it but get a problem, thuis is my code at the mo:
(Header includes removed for clarity)

Removing relevant code never adds clarity.
{
string str
char junk[100];
int MaxLineLength =100;
const int status[] = {EXIT_SUCCESS, EXIT_FAILURE};
ifstream input(FileName.c_str());
bool err(!input);

input >> str;
/* Step to Here: str is *3DSMAX_ASCIIEXPORT */
input >> str;
/* Step to Here: str is ??? */
input >> str;
}



start of Source File:
*3DSMAX_ASCIIEXPORT 200
*COMMENT "AsciiExport Version 2.00 - Tue Apr 06 19:00:45 2004"
*SCENE {
*SCENE_FILENAME "City_vertex_paint.max"
*SCENE_FIRSTFRAME 0

Now the code compiles fine, but when I step through it in the debugger, the
first string reads: *3DSMAX_ASCIIEXPORT, but the next time MSVC++ debugger
gives its value as ???

What about printing the string? Debuggers don't always show things
correctly.
What am i doing wrong?
Also what is the best way to read this into relevant structures, If i define
a class material for instance, can i overload the >> operator for it and
call that after the reading function had read "*MATERIAL 0"??

You can do that. >> is usually used for single input items, not
collections, so it's a bit unconventional, but it should work. You could
also just have a Read() member that does the same thing that the
operator >> would do.

-Kevin






Hi,
It seems that

ifstream input(FileName.c_str());
string str;

input >> str;
/*Step to Here: str is *3DSMAX_ASCIIEXPORT */
input >> str;
* Step to Here: str is ??? */

gives problems but


ifstream input(FileName.c_str());
string str1,str2;

input >> str1
/*Step to Here: str is *3DSMAX_ASCIIEXPORT */
input >> str2
* Step to Here: str is 200

works as expected. Is it nessesary to flush the str before entering new
data?

Michael
 
J

John Harrison

Hi,
It seems that

ifstream input(FileName.c_str());
string str;

input >> str;
/*Step to Here: str is *3DSMAX_ASCIIEXPORT */
input >> str;
* Step to Here: str is ??? */

gives problems but


ifstream input(FileName.c_str());
string str1,str2;

input >> str1
/*Step to Here: str is *3DSMAX_ASCIIEXPORT */
input >> str2
* Step to Here: str is 200

works as expected. Is it nessesary to flush the str before entering new
data?

Michael

No it is not necessary. Either you have a bugged implementation of the
standard library, or you are making some other mistake. Most likely is that
what Kevin told you is true, your debugger is not displaying the string
values correctly. I found exactly the same with my debugger (also MSVC++).

john
 
T

Thomas Matthews

Michael said:
Hi,
I have tried to implement it but get a problem, thuis is my code at the mo:
(Header includes removed for clarity)

{
string str
char junk[100];
int MaxLineLength =100;
const int status[] = {EXIT_SUCCESS, EXIT_FAILURE};
ifstream input(FileName.c_str());
bool err(!input);

input >> str;
/* Step to Here: str is *3DSMAX_ASCIIEXPORT */
input >> str;
/* Step to Here: str is ??? */
input >> str;
}



start of Source File:
*3DSMAX_ASCIIEXPORT 200
*COMMENT "AsciiExport Version 2.00 - Tue Apr 06 19:00:45 2004"
*SCENE {
*SCENE_FILENAME "City_vertex_paint.max"
*SCENE_FIRSTFRAME 0
*SCENE_LASTFRAME 500
*SCENE_FRAMESPEED 30
*SCENE_TICKSPERFRAME 160
*SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000
*SCENE_AMBIENT_STATIC 0.9804 0.9804 0.9804
}
*MATERIAL_LIST {
*MATERIAL_COUNT 13
*MATERIAL 0 {
*MATERIAL_NAME "SF Building"
*MATERIAL_CLASS "Multi/Sub-Object"
*MATERIAL_AMBIENT 0.1000 0.1000 0.1000
*MATERIAL_DIFFUSE 0.0392 0.0392 0.0392
*MATERIAL_SPECULAR 0.9000 0.9000 0.9000
*MATERIAL_SHINE 0.2500
*MATERIAL_SHINESTRENGTH 0.0500
*MATERIAL_TRANSPARENCY 0.0000
*MATERIAL_WIRESIZE 1.0000
*NUMSUBMTLS 6
*SUBMATERIAL 0 {
*MATERIAL_NAME "Top"
*MATERIAL_CLASS "Standard"
*MATERIAL_AMBIENT 0.1000 0.1000 0.1000
*MATERIAL_DIFFUSE 0.0392 0.0392 0.0392
*MATERIAL_SPECULAR 0.9000 0.9000 0.9000
*MATERIAL_SHINE 0.2500
*MATERIAL_SHINESTRENGTH 0.0500
*MATERIAL_TRANSPARENCY 0.0000
*MATERIAL_WIRESIZE 1.0000
*MATERIAL_SHADING Blinn
*MATERIAL_XP_FALLOFF 0.0000
*MATERIAL_SELFILLUM 0.0000
*MATERIAL_FALLOFF In
*MATERIAL_XP_TYPE Filter
}
*SUBMATERIAL 1 {
*MATERIAL_NAME "Bottom"
*MATERIAL_CLASS "Standard"

Now the code compiles fine, but when I step through it in the debugger, the
first string reads: *3DSMAX_ASCIIEXPORT, but the next time MSVC++ debugger
gives its value as ???
What am i doing wrong?
Also what is the best way to read this into relevant structures, If i define
a class material for instance, can i overload the >> operator for it and
call that after the reading function had read "*MATERIAL 0"??
I'm not yet a great fan of streams :)

Thanks & Regards

Michael

Sorry about posting so late, but I've had other work to do.

You are parsing a file that is made up of text lines. The text lines
are delineated by a newline character ('\n'). In these scenarios,
the easier approach would be to read in the whole text line into
a string, then extract the information from a string. The std::string
has better facilities for searching than the I/O streams do.

Here is a sample to get you started:
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using std::ifstream;
using std::string;
using std::cout;
using std::endl;
using std::cerr;


int main(void)
{
ifstream source_file("Source_File.txt");

if (!source_file)
{
cerr << "Error opening \"Source_File.txt\"" << endl;
return EXIT_FAILURE;
}

string text;
string::size_type posn;
while (getline(source_file, text))
{
posn = text.find("3DSMAX_ASCIIEXPORT");
if (posn == string::npos)
{
continue; // Skip lines until that one is found.
}
posn = text.find_first_of(" \t"); // skip past the identifier.
istringstream temp_string_stream(text.substr(posn));
int value;
temp_string_stream >> value; // extract the value.
cout << "Value found is: " << value << endl;
}

return EXIT_SUCCESS;
}


A next step in this evolution would be to have a std::map
container of <string, function pointer>. The strings would
be the identifiers in the data file. The function pointer
would point to a function that processed the identifier.

Example:
#include <map>
using std::map;
typedef void (*Function_Ptr)(const string& s,
const string::size_type& posn);

void AsciiExport(const string& s, const size_type& posn)
{
istringstream temp_string_stream(s.substr(posn));
int value;
temp_string_stream >> value; // extract the value.
cout << "Value found is: " << value << endl;
return;
}

typedef std::map<string, Function_Ptr> Processing_Map;

Processing_Map function_map;

int main(void)
{
function_map["3DSMAX_ASCIIEXPORT"] = AsciiExport;
ifstream source_file("Source_File.txt");

if (!source_file)
{
cerr << "Error opening \"Source_File.txt\"" << endl;
return EXIT_FAILURE;
}

string text;
string::size_type posn;
while (getline(source_file, text))
{
posn = text.find("3DSMAX_ASCIIEXPORT");
if (posn == string::npos)
{
continue; // Skip lines until that one is found.
}
string::size_type end_posn = text.find_first_of(" \t", posn);
Processing_Map::iterator iter;
iter = function_map.find(text.substr(posn, end_posn - posn));
if (iter != function_map.end())
{
(iter->second)(text, end_posn); // execute the processing
// function.
}
}
return EXIT_SUCCESS;
}

After this step, you may want to research the Factory design
pattern (search the web for "Design Pattern Factory").

--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.raos.demon.uk/acllc-c++/faq.html
Other sites:
http://www.josuttis.com -- C++ STL Library book
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,164
Messages
2,570,898
Members
47,439
Latest member
shasuze

Latest Threads

Top