Parsing strategy

I'm designing a configuration file that users can use to customize my program's behavior on initialization.

Commands have the $ prefix so I can process commands:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ifstream fin("configuration.txt");
string message;
while (getline(fin, message))
{
    if (message[0]=='$')
    {
        if (message == "$RUN")      run();
        if (message == "$SEND")     send();
        if (message == "$WAIT 76")  wait(76);
        if (message == "$SPD 34.7") set_speed(34.7);
    }
    else
    {
        //Other stuff here
    }
}


Now my question is on lines 9 & 10. I'm looking for a strategy to identify the command and then extract the number beside it as the argument.

I'm thinking I may need to do this, but it isn't a very elegant solution:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
float Check_WAIT(string message)
{
	float parameter = 0; // Default parameter
	bool decimalFound = false;
	float decimalPlace = 1.f;

	if (message[1] == 'W' && message[2] == 'A' && message[3] == 'I' && message[4] == 'T')
	{
		for (int i=5; message[i]!='\0'; i++)
		{
			if (message[i] == '.') decimalFound = true;
			else if (message[i] >='0' && message[i] <= '9')
			{
				if (!decimalFound)
				{
					parameter *= 10;
					parameter += message[i]-'0';
				}
				else
				{
					decimalPlace *= 0.1f;
					parameter += float(message[i]-'0') * decimalPlace;
				}
			}
		}
		return parameter;
	}

	return -1.f;
}


Does anyone have any better ideas?
Find last occurance of space and use atof function in math.h header file.
copy the string that is after last space and convert it to float. (integers can be converted to floats easely)
Ahh, so there is already a library function that converts strings to doubles. Excellent, I didn't know about that. (It's in cstdlib : http://cplusplus.com/reference/clibrary/cstdlib/atof/)

I've done some compacting and I have this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <cstdlib>
#include <fstream>
#include <string>
bool isWAIT(string message, double &param)
{
	if (message[1] == 'W' && message[2] == 'A' && message[3] == 'I' && message[4] == 'T')
	{
		param = atof(message.c_str());
		return true;
	}
	return false;
}

bool isSPD(string message, double &param)
{
	if (message[1] == 'S' && message[2] == 'P' && message[3] == 'D')
	{
		param = atof(message.c_str());
		return true;
	}
	return false;
}

void myFunc()
{
	ifstream fin("configuration.txt");
	string message;
	while (getline(fin, message))
	{
		if (message[0]=='$')
		{
			double param;
			if (message == "$RUN")      run();
			if (message == "$SEND")     send();
			if (isWAIT(message, param)) wait(param);
			if (isSPD(message, param))  set_speed(param);
		}
		else
		{
			//Other stuff here
		}
	}
}


Now is there a shortcut for lines 6 and 16? A standard function that would compare two strings or depricate one?
1
2
3
4
string a = "$WAIT 34.61";
string b = "$WAIT";

if ( AContainsB(a, b)) param = atof(AWithoutB(a,b))


EDIT: I think I've answered my own questions.
EDIT: Yep, got it. I just had to write the two simple functions above.
Last edited on
For this I generally use std::istringstream() to extract the parameters. Here is some (untested) code to show you an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

std::string& trim(std::string& s, const char* t = " \t\n\r\f\v")
{
	s.erase(0, s.find_first_not_of(t));
	s.erase(s.find_last_not_of(t) + 1);
	return s;
}

bool error(size_t number, const std::string& messge)
{
	std::cout << "Error on line " << number;
	std::cout << ": " << message << '\n';
	return false; // always return error
}

bool command(const std::string& line, size_t number)
{
	std::istringstream iss(line);

	std::string cmd;
	if(!(iss >> cmd))
		return error(number, "Command expected.");

	if(cmd == "RUN") return run();
	else if(cmd == "SEND") return send();
	else if(cmd == "WAIT")
	{
		int time;
		if(!(iss >> time))
			return error(number, "Command WAIT expected parameter: WAIT n");
		return wait(time);
	}
	else if(cmd == "SPD")
	{
		double value;
		if(!(iss >> value))
			return error(number, "Command SPD expected parameter: SPD n");
		return spd(value);
	}
	else if(cmd == "POINT")
	{
		double x, y;
		if(!(iss >> x >> y))
			return error(number, "Command POINT expected parameters: POINT x y");
		return point(x, y);
	}

	return error(number, "Unknown command.");
}

int main()
{
	std::ifstream ifs("config.txt");

	size_t number;
	std::string line;

	while(std::getline(ifs, line))
	{
		++number;
		if(!trim(line).empty() && line[0] == '$')
			command(line.substr(1), number);
	}
}


EDIT: Fixed some obvious bugs
Last edited on
Brilliant! This makes getting each argument much easier and atof() is not required.
Stewbond wrote:
Now is there a shortcut for lines 6 and 16? A standard function that would compare two strings or depricate one?


Try string::find(): http://cplusplus.com/reference/string/string/find/
Topic archived. No new replies allowed.