Thread: C++ ini file reader problems

  1. #1
    Registered User
    Join Date
    Mar 2006
    Location
    USA::Colorado
    Posts
    155

    C++ ini file reader problems

    Hey.

    I'm trying to make a C++ program that will read a .INI file and process it into three parts. Headers, variables, and values.

    Here's the INIReader.h file
    Code:
    #ifndef INIREADER_H
    #define INIREADER_H
    
    #include <iostream>
    #include <vector>
    #include <math.h>
    
    using namespace std;
    
    struct headNode
    {
    	char header[255];
    	vector<char*> variables;
    	vector<char*> values;
    };
    
    
    class CINIReader
    {
    	//member variables
    public:
    	char m_filename[255];
    	vector<headNode> headers;
    
    	//member functions
    public:
    	CINIReader(); //constructor
    	~CINIReader(); //destructor
    
    	bool SetFile ( char file[] );
    	bool ParseFile ( char file[] );
    
    	int GetInt ( char varname[], char header[] );
    	char GetStr ( char varname[], char header[] );
    	bool GetBool ( char varname[], char header[] );
    
    	int CharToInt ( char var[] );
    
    private:
    	bool StripTags ( const char *source, size_t bytes,  char *result );
    	bool SplitString ( const char *source, size_t bytes, const char delim, char * variable, char *value );
    
    };
    
    #endif
    Here's the INIReader.cpp file
    Code:
    #include "INIReader.h"
    #include <fstream>
    
    CINIReader::CINIReader()
    {
    
    }
    
    CINIReader::~CINIReader()
    {
    
    }
    
    //member functions
    
    bool CINIReader::SetFile ( char file[255] )
    {
    	strcpy_s ( m_filename, 255, file );
    
    	return ParseFile ( file );
    }
    
    bool CINIReader::ParseFile ( char file[] )
    {
    	ifstream in ( file );
    
    	if ( !in.is_open() )
    		return false;
    
    	char buffer[255];
    	char buffer2[255];
    	char buffer3[255];
    
    	bool alreadyOne = false;
    
    	headNode *hn;
    
    	while ( in.good() )
    	{
    		in.getline ( buffer, 255 );
    		
    		switch ( buffer[0] )
    		{
    			//start of a new header
    		case '[':
    			if ( alreadyOne )
    			{
    				headers.push_back ( *hn );
    				delete hn;
    			}
    
    			hn = new headNode;
    
    			StripTags ( buffer, 255, hn->header );
    			alreadyOne = true;
    
    			break;
    
    			//all comments...
    		case ';':
    		case NULL:
    		case ' ':
    		case '#':
    			break;
    
    			//add a line for the variables
    		default:
    			SplitString ( buffer, 255, '=', buffer2, buffer3 );
    			hn->variables.push_back ( buffer2 );
    			hn->values.push_back ( buffer3 );
    			break;
    		}
    	}
    
    	if ( alreadyOne )
    	{
    		headers.push_back ( *hn );
    		delete hn;
    	}
    
    	return true;
    }
    
    bool CINIReader::SplitString ( const char *source, size_t bytes, const char delim, char *variable, char *value )
    {
    	const char *s = source;
    	char *var = variable;
    	char *val = value;
    	bool afterEqualSign = false;
    	size_t b = 0;
    
    	//go through the entire source string
    	for ( size_t i = 0; i < strlen ( source ); i++ )
    	{
    		//break if necessary for security reasons (buffer overflow)
    		if ( b >= bytes )
    		{
    			return false;
    		}
    
    		if ( s[i] == delim )
    		{
    			afterEqualSign = true;
    		}
    		else if ( afterEqualSign && s[i] != '=' )
    		{
    			*val = s[i];
    			val++;
    		}
    		else if ( !afterEqualSign )
    		{
    			*var = s[i];
    			var++;
    		}
    
    		b++;
    	}
    
    	//end of strings
    	*val = '\0';
    	*var = '\0';
    
    	return true;
    }
    
    //strips the [ and ] from a header. for example [test] would become test.
    bool CINIReader::StripTags ( const char *source, size_t bytes, char *result )
    {
    	const char *s;
    	char *r;
    
    	size_t b = 0;
    
    	s = source;
    	r = result;
    
    	int i = 1;
    	while ( s[i] != ']' )
    	{
    		if ( b >= bytes )
    			return false;
    
    		*r = s[i++];
    		r++;
    
    		b++;
    	}
    
    	*r = '\0';
    
    	return true;
    }
    
    //converts ascii strings to integers (i wanted to see if I could do it :D)
    int CINIReader::CharToInt ( char str[] )
    {
    	size_t stringSize = strlen ( str );
    
    	int num = 0;
    	int buffer;
    
    	for ( size_t i = 0; i < stringSize; i++ )
    	{
    		buffer = static_cast<int>(str[i]);
    		buffer = buffer - 48;
    
    		num += buffer * (int)pow ( (double)10, (double)( ( stringSize - 1 ) - i ) );
    	}
    
    	return num;
    }
    Here's the main.cpp file
    Code:
    #include <iostream>
    #include "INIReader.h"
    #include <vector>
    
    using namespace std;
    
    void print ( const char *c )
    {
    	const char *p;
    	for ( p = c; *p != '\0'; p++ )
    	{
    		cout << *p;
    	}
    }
    
    int main ( void )
    {
    	CINIReader read;
    
    	read.SetFile ( "test.txt" );
    
    
    	for ( int i = 0; i < read.headers.size(); i++ )
    	{
    		print ( read.headers.at(i).header );
    
    		for ( int j = 0; j < read.headers.at(i).variables.size(); j++ )
    		{
    			cout << "variable: ";
    			print ( read.headers.at(i).variables.at(j) );
    			cout << " Value: ";
    			print ( read.headers.at(i).values.at(j) );
    			cout << endl;
    		}
    
    		cout << endl;
    	}
    
    	system("pause");
    
    	return 0;
    }
    Here's the test.txt file (acts as an INI file, just with a diff ext)
    Code:
    [head 1]
    var1=this is a variable
    var2=hopefully this will work
    var3=nope. its not...
    
    ;this is a comment, it will be ignored by the program
    #this is a comment, it will be ignored by the program
    
    [head2]
    variable=3
    Here's my output:
    Code:
    head 1variable: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
    ╠╠╠╠╠╠╠╠⌠√↨ Value:
    variable: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
    ╠╠⌠√↨ Value: ►²↨
    variable: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
    ╠╠⌠√↨ Value: ►²↨
    
    head2variable: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
    ╠╠╠╠╠╠╠⌠√↨ Value: ►²↨
    
    Press any key to continue . . .
    Any reason why the variable and value are complete garbage?

    Thanks in advance,

    Guitarist809
    ~guitarist809~

  2. #2
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    Yep... there is no '\0' terminator. By the way, you chose probably the most horrible way of doing this possible... Ok, maybe not as horrible as making a linked list or tree or something more incongruous.

    Check out stringstream.

  3. #3
    Registered User
    Join Date
    Mar 2006
    Location
    USA::Colorado
    Posts
    155
    Quote Originally Posted by master5001 View Post
    Yep... there is no '\0' terminator. By the way, you chose probably the most horrible way of doing this possible... Ok, maybe not as horrible as making a linked list or tree or something more incongruous.

    Check out stringstream.
    Yea, I was actually trying to experiment with pointers and other variables. I honestly don't have the slightest idea how strings work and how pointers are associated with them. I was trying to see if I could "force" the strings to end with the \0 terminator, which doesn't even exist apparently.

    I'll go have a look at stringstream to see if there's an easier way of doing this.
    ~guitarist809~

  4. #4
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    I think you will find stringstream so much easier to manage than this code. I can spend a lot of time pointing out things that could be improved. I see the code at least functionally works halfway as it is supposed to, so you get an A for effort. However, so much of this type of stuff is just you re-inventing the wheel.

    I know for personal advancement in programming re-inventing the wheel can be fun. But I don't think you are having too much fun at this point.

  5. #5
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    You can replace your print function with just "cout << c"
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  6. #6
    Registered User
    Join Date
    Mar 2006
    Location
    USA::Colorado
    Posts
    155
    ok, I completely re-did it. Is what I did any better than before?

    INIFile.h
    Code:
    #ifndef INIFILE_H
    #define INIFILE_H
    
    #include <vector>
    #include <map>
    #include <string>
    #include <fstream>
    
    using namespace std;
    
    struct KeyNode
    {
    	string key;
    	map<string, string> vars;
    };
    
    class CINIFile
    {
    public:
    	vector<KeyNode> m_Keys;
    
    public:
    	CINIFile();
    	~CINIFile();
    
    	bool LoadFile ( string filename );
    	bool ParseFile ( string filename );
    
    
    	bool GetIntValue ( string key, string var, int &val );
    	bool GetStringValue ( string key, string var, string &val );
    	bool GetBoolValue ( string key, string var, bool &val );
    
    	bool AddKey ( string key );
    
    	bool InsertKeyValues ( string key, string var, string val );
    	bool InsertKeyValues ( string key, map<string, string> m );
    	bool InsertKeyValues ( string var, string val );
    	bool InsertKeyValues ( map<string,string> m );
    
    	string StripChars ( string tags, string source );
    	
    	map<string, string> SeparateAtChar ( const char separator, string source );
    };
    
    #endif
    INIFile.cpp
    Code:
    #include "INIFile.h"
    #include <iostream>
    
    CINIFile::CINIFile()
    {
    
    }
    
    CINIFile::~CINIFile()
    {
    
    }
    
    //Main Functions
    
    bool CINIFile::LoadFile ( string filename )
    {
    	return ParseFile ( filename );
    }
    
    bool CINIFile::ParseFile ( string filename )
    {
    	ifstream in ( filename.c_str() );
    
    	string buffer;
    
    	if ( !in.is_open() )
    	{
    		return false;
    	}
    
    	while ( in.good() )
    	{
    		getline ( in, buffer, '\n' );
    
    		//
    		//Figure out what to do based on the first character
    		//
    		if ( buffer.empty() )
    		{
    		}
    		else if ( buffer.at(0) == '[' ) //start new header
    		{
    			AddKey ( StripChars ( "[]", buffer ) );
    		}
    		else if ( buffer.at(0) == ';' || buffer.at(0) == '#' || buffer.at(0) == ' ' || buffer.at(0) == '\n') //comments or nothing
    		{
    		}
    		else //everything else!
    		{
    			InsertKeyValues ( SeparateAtChar ( '=', buffer ) );
    		}
    	}
    
    	return true;
    }
    
    bool CINIFile::AddKey ( string key )
    {
    	KeyNode kn;
    
    	kn.key = key;
    
    	m_Keys.push_back ( kn );
    
    	return true;
    }
    
    bool CINIFile::InsertKeyValues ( string key, string var, string val )
    {
    	vector<KeyNode>::iterator i;
    
    	for ( i = m_Keys.begin(); i != m_Keys.end(); ++i )
    	{
    		if ( i->key.compare ( key ) )
    		{
    			i->vars.insert ( map<string, string>::value_type ( var, val ) );
    		}
    	}
    
    	return true;
    }
    
    bool CINIFile::InsertKeyValues ( string var, string val )
    {
    	m_Keys.back().vars.insert ( map<string, string>::value_type ( var, val ) );
    
    	return true;
    }
    
    bool CINIFile::InsertKeyValues ( map<string,string> m )
    {
    	for ( map<string, string>::iterator i = m.begin(); i != m.end(); ++i )
    	{
    		m_Keys.back().vars.insert ( map<string, string>::value_type ( i->first, i->second ) );
    	}
    	return true;
    }
    
    bool CINIFile::InsertKeyValues ( string key, map<string,string> m )
    {
    	vector<KeyNode>::iterator i;
    
    	for ( i = m_Keys.begin(); i != m_Keys.end(); ++i )
    	{
    		if ( i->key.compare ( key ) )
    		{
    			for ( map<string, string>::iterator j = m.begin(); j != m.end(); ++j )
    			{
    				i->vars.insert ( map<string, string>::value_type ( j->first, j->second ) );
    			}
    		}
    	}
    
    	return true;
    }
    
    string CINIFile::StripChars ( string tags, string source )
    {
    	string stripped;
    
    	bool fail;
    
    	for ( string::iterator i = source.begin(); i != source.end(); ++i )
    	{
    		fail = false;
    
    		for ( string::iterator j = tags.begin(); j != tags.end(); ++j )
    		{
    			if ( *i == *j )
    				fail = true;
    		}
    
    		if ( fail == false )
    		{
    			stripped.push_back ( *i );
    		}
    	}
    
    	return stripped;
    }
    
    map<string, string> CINIFile::SeparateAtChar ( const char separator, string source )
    {
    	string a, b;
    	map<string, string> m;
    
    	bool afterSeparator = false;
    
    	for ( string::iterator i = source.begin(); i != source.end(); ++i )
    	{
    		if ( *i == separator )
    		{
    			afterSeparator = true;
    		}
    		else if ( afterSeparator == true && *i != separator )
    		{
    			b.push_back ( *i );
    		}
    		else
    		{
    			a.push_back ( *i );
    		}
    	}
    
    	m.insert ( map<string, string>::value_type ( a, b ) );
    
    	return m;
    }
    It took me like an hour and a half to write this (and it works). I'm just curious if it's any more effeciant than the other one.
    ~guitarist809~

  7. #7
    The larch
    Join Date
    May 2006
    Posts
    3,573
    The other one didn't even work (you seemed to be pushing pointers to function-local arrays into a vector)?

    Anyway, I doubt that something like a INI file parser even needs to be as efficient as possible. Rather focus on parts of code that are indeed called thousands of times, and otherwise aim for robustness and simplicity of code.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Or you could use an existing INI parser. There's more than one out there. I'd like to recommend Boost.PropertyTree, but that library's documentation is in a sorry state.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. File transfer- the file sometimes not full transferred
    By shu_fei86 in forum C# Programming
    Replies: 13
    Last Post: 03-13-2009, 12:44 PM
  2. File Writing Problem
    By polskash in forum C Programming
    Replies: 3
    Last Post: 02-13-2009, 10:47 AM
  3. Data Structure Eror
    By prominababy in forum C Programming
    Replies: 3
    Last Post: 01-06-2009, 09:35 AM
  4. Basic text file encoder
    By Abda92 in forum C Programming
    Replies: 15
    Last Post: 05-22-2007, 01:19 PM
  5. Unknown Memory Leak in Init() Function
    By CodeHacker in forum Windows Programming
    Replies: 3
    Last Post: 07-09-2004, 09:54 AM