Sign in to follow this  
jrmiller84

Loading Settings from Text Doc

Recommended Posts

Hello everyone, Today I've been working on a loading routine for my game. Since this is just a basic 2d scroller I decided to use text files to hold the settings. I can open the text file just fine and read the characters from it but I am having trouble splitting the strings into usable data. My setting text file looks as follows:
NAME=Megaman
TYPE=PLAYER
X_POSITION=400
Y_POSITION=400
HEIGHT=100
WIDTH=100
TEXTURE=MegamanFit.bmp,14,14,5
RUN=0,10,1,20

Now as I said, I am having trouble splitting the strings into usuable segments. The way I read my data looks as follows:
	ifstream myfile ("example.txt");

	if(myfile.is_open()) {

		while (!myfile.eof()) {

			myfile >> temp;

                        //process command data using temp here, etc etc

                }

         }

Now this reads the strings in a char format which I am sure you guys may not agree with. Everywhere I look everyone says to use a string data type and my problem with that is I can not get the c_str() method of the string data type to convert to a regular char * to work with my methods already in use. I would honestly prefer to use the string data type because it is a lot easier to work with when splitting string. When I do try to use it I get "cannot convert parameter 1 from 'const char *' to 'char *'." Any help would be awesome on how to either split chars effectively or get string data types to convert correctly to chars. Oh, also, Ive been splitting chars using the strtok() method but it seems to be more trouble than it's worth so I'm hoping to ditch that method if I get a resolution to the other.

Share this post


Link to post
Share on other sites
I hope I'm not alone on this, but C++ I/O sucks. I would consider using C functions.

fscanf(...) - works just like printf (more like fprintf). You tell the function which file you are reading from, then you give it a printf-style format string (char*). Then you tell it where to read in the variables by passing pointers to each variable.

Lets say you have this text file:
Megaman
PLAYER
400
400

char buffer1[256], buffer2[256];
float x, y;
fscanf(file, "%s %s %f %f", buffer1, buffer2, &x, &y);

Then convert buffer2 to whatever enumerated type you want. fscanf works great with predefined types, but will not work with enumerations, classes, etc. One thing that I like is all white space is the same. The "%s %s..." will read the first string "Megaman", then it will skip over any combination of spaces, tabs, or newlines before reading the 2nd string.

If you want to save/load a struct/class of your own, use fwrite/fread functions.

Now I am in no way suggesting you compile your projects in C. Just use its functions and as long as you don't horribly mix-and-match C with C++, everything will work out fine.

Share this post


Link to post
Share on other sites
I am by no means a C/C++ expert, but I think I am starting to develop that same opinion. Ive almost got this thing finished and this is the one thing thats been more difficult than the project itself. I'm going to rewrrite a little bit of that procedure and see if it works out better. The reason I was using the "Setting=Value" format is because it is intended to have a level editor associated with it and I thought it would be more user friendly to see those settings. But I plan on making a readme with it so I suppose if they follow instructions they will be ok. Thanks for the help and I'll post my results soon!

Share this post


Link to post
Share on other sites
You can make
PROPERTIES value
so it is easier for c++ to read, if you dont want to
write the parser.

NAME Megaman

then
fin >> string1; //get NAME
fin >> string2; //get Megaman

Share this post


Link to post
Share on other sites
Here's my suggestion:

Let's say you have your data as you showed it:

NAME=Megaman
TYPE=PLAYER
X_POSITION=400
Y_POSITION=400
HEIGHT=100
WIDTH=100
TEXTURE=MegamanFit.bmp,14,14,5
RUN=0,10,1,20

Now the format you have is as follows:
Identifier = token1, token2, ..., tokenN

Now, you know that the order is as such and each line contains one definition, right? What if you were to parse by tokens, specifically the =, the comma, and the newline? Then you would have all of your tokens in a convient form. On this thread I have a little collection of useful standard C++ library functions. Using a few of those, I came up with this:


#include <iostream>
#include <string>
#include <fstream>
#include <vector>

std::vector<std::string> TokenizeString(const std::string& str, const std::string& delim)
{
using namespace std;
vector<string> tokens;
size_t p0 = 0, p1 = string::npos;
while(p0 != string::npos)
{
p1 = str.find_first_of(delim, p0);
if(p1 != p0)
{
string token = str.substr(p0, p1 - p0);
tokens.push_back(token);
}
p0 = str.find_first_not_of(delim, p1);
}
return tokens;
}

std::vector<std::string> FileToVector(const std::string& filename, const std::string& tokens = "\n")
{
std::ifstream fin(filename.c_str());
return TokenizeString(std::string((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>()), tokens);
}

int main(int argc, char* argv[])
{
// Load this file and tokenize it into a vector using \n
std::vector<std::string> linetokens = FileToVector("test.txt", "\n");

// Make a new vector of vectors based on the number of lines there are in the file
std::vector< std::vector<std::string> > tokens(linetokens.size());

// Loop though the lines and tokenize them
for(int x = 0; x < linetokens.size(); x++)
{
// Now parse each individual line by the = and , delimeters,
// we know that tokens[x][0] will be the name of the setting, such as "NAME" or "TYPE"
tokens[x] = TokenizeString(linetokens[x], ",=");
}

// Now go though all of the tokens
for(int x = 0; x < tokens.size(); x++)
{
// If we have a Name
if(tokens[x][0] == "NAME")
{
// There should be a total of 2 items with a NAME
if(tokens[x].size() != 2)
exit(-1);

// We know there should be only 1 parameter after a name
std::cout << "The name is: " << tokens[x][1] << std::endl;
}

// If we have a type
else if(tokens[x][0] == "TYPE")
{
// There should be a total of 2 items with a TYPE
if(tokens[x].size() != 2)
exit(-1);

// We know there should be only 1 parameter after a type
std::cout << "The type is: " << tokens[x][1] << std::endl;
}
// ... Add the rest here!
}

return 0;
}



Now, if you go though that you can see exactly what is going on. 1st, I go though and load the file into a vector based on new lines, since each entry is only on 1 line. Next, I go though all lines and break up the tokens based on the = and the , since parameters are seperated by ,'s and the entry is seperated by the =. What results is a vevctor of strings that contain all of the tokens.

For example, the line "TEXTURE=MegamanFit.bmp,14,14,5" would be stored as:

TEXTURE
MegamanFit.bmp
14
14
5

In the vector. Also, you cam see the crude error checking added in, if you know the NAME should only have 1 parameter after it, you can test the size to make sure nothing was added or is missing (don't forget that the identifier itself counts as part of the size, so 1 parameter = 2 items, 2 params = 3 items.)

I hope that helps some, you can make a few improvements as you need, but that's the general idea you are after. Good luck on your game! [smile]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this