reading from text files

Started by
9 comments, last by Gaiiden 19 years, 6 months ago
hi all, I've been wrestling with the documentation for ages now but still can't solve my problem with an elegent method. I have a struct containing data members:

struct texture_details {
        std::string		filename;
	int			block_num;
	int			block_ref;
	int			block_size;
	float		tex_time;
};



as you can see not all these members are of type string, but I want to read in the values for these from a text file when the program starts up. Each texture_details struct is being initialised with a line of text in this text file, with commas as teh delimiters so the file tex_det.dat is for example: pic1.bmp, 1, 2, 1, 0.4 pic2.bmp, 2, 2, 1, 0.23 and so on. So far I have been using getline for the first member which is of type string. so my 'reading-in' code is:

std::ifstream tex_file ("texs.dat");
for (int i = 0; i<num_of_lines; i++)
	{
		std::getline(tex_file, texture_details_array.filename, ',');
                //read in the other 4 numbers here
        }


however since the other members are not of type string getline doesn't want to feed the data into the other members. What should I be doing to feed data from teh text file into these members? Should I be using some kind of casting?
Advertisement
There are a couple of ways you can do this. One would be to have each number on it's own line separate from the bmp file name, and then call the atoi() function on lines containing numbers.

Or, you could parse the line after it is read into your program using ',' as a token. Then call atoi() on all numbers that are found.

Or you could remove the commas, and instead of reading the entire line, only read a string at a time, and only call atoi when a number is encountered.

Lot's of ways to do this.

Edit
----

Here's my open file code for my map editor

int Open_Map(char * filename, HWND hwnd){    ifstream fin(filename);    char buf[80];    int i;        /* figure out how many tiles this map contains */    do {        fin.getline(buf,80);    } while (!isdigit(buf[0]));    int totalTiles = atoi(buf);    /* done */        /* find the height/width of the tiles        and find how many rows/columns      */    do {        fin.getline(buf,80);    } while (!isdigit(buf[0]));    TILE_WIDTH = atoi(buf);    fin.getline(buf,80);    TILE_HEIGHT = atoi(buf);    fin.getline(buf,80);    TILES_WIDE = atoi(buf);    fin.getline(buf,80);    TILES_HIGH = atoi(buf);     /* done =) */    /* allocate enough memory to hold the tiles */    delete [] maptiles;    maptiles = new MAPTILES[totalTiles];    /* done */        /* put the file pointer in the right spot */    do {        fin.getline(buf,80);    } while (!isdigit(buf[0]));    /* done */        /* read data into the maptiles struct */    for(i = 0;i<totalTiles;i++)    {        maptiles.x = atoi(buf);        fin.getline(buf,80);        maptiles.y = atoi(buf);        fin.getline(buf,80);        maptiles.collisionLayerOne = atoi(buf);        fin.getline(buf,80);        maptiles.collisionLayerTwo = atoi(buf);        fin.getline(buf,80);        maptiles.animLayerOne = atoi(buf);        fin.getline(buf,80);        maptiles.animLayerTwo = atoi(buf);        fin.getline(buf,80);        maptiles.layerone = buf;        fin.getline(buf,80);        maptiles.layertwo = buf;             // get to the next tile by skipping two lines        fin.getline(buf,80);        fin.getline(buf,80);    }            fin.close();       RedrawWindow(hwnd,NULL,NULL,RDW_UPDATENOW);    Draw_Map(hwnd);        return 0;}


as you can see, I've decided to have each number on it's own line, and then I call atoi on lines containing numbers.
maybe I'm misunderstanding you ontheheap, but isnt the problem that getline won't read into a variable not of type string, rather than the actual problem of converting the type. if I set a std::string equal to a integer, doesnt the string class automatically convert the number to a string anyway?

Surely the problem is that texture_details_array.block_num is of type int, and when that is entered as an argument into getline to be fed into getline doesnt like it.
ah your edit has clarified my misunderstanding. So you're saying I should create a buffer, read into that, and then use that to read into the struct, rather than try to read directly into the struct right? Is this an efficient way of doing this?
I figured some actual code with make my crappy description make more sense =)

Edit: I'm sure there are better ways.. However, atoi() is the only way I know how to convert from a string to an integer. Another way to do this, however, would be to save the file as binary instead of text. clicky. I'm probably going to switch to this method myself.
I believe the actual C++ way of doing this is to use std::stringstream. Once you have your string out the way, simple put all the remaining data into the stringstream object and then use it like cin to get your data. E.g

#include <iostream>#include <sstream>#include <string>int main(){	const std::string fileData = "pic1.bmp, 1, 2, 1, 0.4";	std::string filename;	int block_num, block_ref, block_size;	float tex_time;	std::stringstream converter(fileData); //Construct stringstream with the contents of the file.	std::getline(converter, filename, ','); //Use getline so that stringstream doesn't stop at spaces	char skipComma; //Used to skip the comma	converter >> block_num >> skipComma >> block_ref >> skipComma >> block_size >> skipComma >> tex_time;	std::cout << "filename:\t" << filename << std::endl;	std::cout << "block_num:\t" << block_num << std::endl;	std::cout << "block_ref:\t" << block_ref << std::endl;	std::cout << "block_size:\t" << block_size << std::endl;	std::cout << "tex_time:\t" << tex_time << std::endl;	system("PAUSE");	return 0;}


A better idea would probably to use a tab to seperate the int/floats as stringstream will skip whitespace, but not the comma.
Thanks desertcube, I wasn't aware of that.
Quote:Original post by bobster
pic1.bmp, 1, 2, 1, 0.4
pic2.bmp, 2, 2, 1, 0.23

If I had a text file like that, first of all I would change it to this:

pic1.bmp 1 2 1 0.4
pic2.bmp 2 2 1 0.23

and then use this code to read it in:
ifstream file("data.txt");if (!file.is_open())    // oh teh nos!1!while (!file.eof()){    texture_details TexDet;    file >> TexDet.filename         >> TexDet.block_num         >> TexDet.block_ref         >> TexDet.block_size         >> TexDet.tex_time;    // store TexDet or something}file.close();

This works because the >> reads in text up to a space, so your data is automatically delimited without having to use commas. It'll also (as you can see) store data as a string, int or float (okay - never tried float myself but I assume it works).

One problem does exist of course. How do you use spaces inside data? So like:

my pic2.bmp 2 2 1 0.23

This would cause an error because "my" would be loaded into texture_details::filename, and "pic.bmp" would be loaded into texture_details::block_num thanks to that space. Instead, use an underscore:

my_pic2.bmp 2 2 1 0.23

and then use this code to erase the underscore in the string:
// substitute any "_" for spacesint iPos = anim->m_strName.find("_");while (iPos < string::npos){	anim->m_strName.replace(iPos, 1, " ");	iPos = anim->m_strName.find("_");}

Drew Sikora
Executive Producer
GameDev.net

thanks so much for these great solutions, worked a treat.
Actually Gaiiden, you reminded me that he doesn't need to use a stringstream.
The code I gave is still valid, but you can use the ifstream object instead of the stringstream. Obviousely, you'd have to make it a little more robust, but the principle should be the same, use a dummy char to get rid of the comma.

Dunno why I didn't see that!?

This topic is closed to new replies.

Advertisement