C Board  

Go Back   C Board > General Programming Boards > C++ Programming

Reply
 
LinkBack Thread Tools Display Modes
Old 09-03-2008, 07:08 PM   #1
Registered User
 
Join Date: Mar 2006
Location: USA::Colorado
Posts: 148
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~
guitarist809 is offline   Reply With Quote
Old 09-03-2008, 07:12 PM   #2
Banned
 
master5001's Avatar
 
Join Date: Aug 2001
Location: Visalia, CA, USA
Posts: 3,699
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.
master5001 is offline   Reply With Quote
Old 09-03-2008, 07:17 PM   #3
Registered User
 
Join Date: Mar 2006
Location: USA::Colorado
Posts: 148
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~
guitarist809 is offline   Reply With Quote
Old 09-03-2008, 07:27 PM   #4
Banned
 
master5001's Avatar
 
Join Date: Aug 2001
Location: Visalia, CA, USA
Posts: 3,699
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.
master5001 is offline   Reply With Quote
Old 09-03-2008, 07:55 PM   #5
Registered User
 
Join Date: Apr 2006
Posts: 1,193
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.
King Mir is offline   Reply With Quote
Old 09-04-2008, 01:07 AM   #6
Registered User
 
Join Date: Mar 2006
Location: USA::Colorado
Posts: 148
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~
guitarist809 is offline   Reply With Quote
Old 09-04-2008, 03:42 AM   #7
The larch
 
Join Date: May 2006
Posts: 3,082
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.

Quote:
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).
anon is offline   Reply With Quote
Old 09-04-2008, 06:02 AM   #8
Cat without Hat
 
CornedBee's Avatar
 
Join Date: Apr 2003
Posts: 8,439
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
CornedBee is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Forum Jump

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


All times are GMT -6. The time now is 02:37 AM.


Powered by vBulletin® Version 3.8.1
Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.3.0 RC2

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22