![]() |
| | #1 |
| Registered User Join Date: Mar 2006 Location: USA::Colorado
Posts: 148
| C++ ini file reader problems 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
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;
}
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;
}
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 Code: head 1variable: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠╠⌠√↨ Value: variable: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠⌠√↨ Value: ►²↨ variable: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠⌠√↨ Value: ►²↨ head2variable: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ ╠╠╠╠╠╠╠⌠√↨ Value: ►²↨ Press any key to continue . . . Thanks in advance, Guitarist809
__________________ ~guitarist809~ |
| guitarist809 is offline | |
| | #2 |
| Banned 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 | |
| | #3 | |
| Registered User Join Date: Mar 2006 Location: USA::Colorado
Posts: 148
| Quote:
I'll go have a look at stringstream to see if there's an easier way of doing this.
__________________ ~guitarist809~ | |
| guitarist809 is offline | |
| | #4 |
| Banned 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 | |
| | #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 | |
| | #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
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;
}
__________________ ~guitarist809~ |
| guitarist809 is offline | |
| | #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:
| |
| anon is offline | |
| | #8 |
| Cat without Hat 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 | |
![]() |
| Thread Tools | |
| Display Modes | |
|
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 |