Reputation: 1479
I have been modding Valve games for quite a while, but one that I have never been good at in the least is using the char arrays to manipulate strings. I would REALLY like to improve considering how much time I waste on them. Obviously using proper Strings would be nice, but since all of the SDK functions return char *, or take it as args, it doesn't make much since to convert back and fourth. Does anyone have any good links as far as understanding them better? Most of what I find on Google has just been snippets.
Also, I am trying to parse a very simple text file. Basically the contents look like this...
PatchVersion=1.1.1.2 ProductName=l4d appID=440
I want to get the PatchVersion and ProductName. My code looks something like this, but really just make lack of proper knowledge has left me stumped. strtok only retrieves the token prior to the '=' sign, strchr gives me a pointer to the location of it, but just don't know a good method.
bool ParseSteamFile()
{
FileHandle_t file;
file = filesystem->Open("steam.inf", "r", "MOD");
if(file)
{
int size = filesystem->Size(file);
char *line = new char[size + 1];
while(!filesystem->EndOfFile(file))
{
char *subLine = filesystem->ReadLine(line, size, file);
Msg("SUBLINE: %s\n", subLine);
char *buffer = "";
if(strstr(subLine, "PatchVersion"))
{
char *c = strtok(subLine, "=");
while(c != NULL)
{
Msg("Token: %s\n", c);
c = strtok(subLine, "=");
}
}
}
}
}
Upvotes: 3
Views: 266
Reputation: 248149
You may want to take a look at Boost's String Algorithms library, which enables similar functionality to what std::string exposes
on arbitrary char sequences, such as C strings.
Upvotes: 0
Reputation: 106216
Obviously using proper Strings would be nice, but since all of the SDK functions return char *, or take it as args, it doesn't make much since to convert back and fourth.
strings have a constructor and operator= from const char*, so it's very easy to store the SDK function results in strings, and the .c_str() member function allows string results to be passed as SDK functions arguments. Really, where your performance requirements aren't so high that you need to avoid heap usage, std::string is worth using just for the benefits of having it automatically grow to fit the data you're handling and releasing the memory when the string goes out of scope. You tend to avoid a lot of small bugs and limitations by using std::string. You can code around C-style strings using asprintf() (if your system provides it, else some hackery with sprintf() and/or snprintf(), malloc(), realloc() etc), and use smart-pointers to get the exception-safe and automatic memory deallocation, but it's still clumsy. stringstreams are also worth using for your input parsing and output formatting... they have a .str() member, and you can chain .c_str() to that to get a const char*.
Where you want stack-variable performance levels (and don't mind having an upper limit on the data size, or a fall-back to heap for large strings), do use the sscanf()
function mentioned in other solutions. You've already received good advice about tokenising the inputs by whitespace, after which you can use strchr to find the first '='.
Upvotes: 0
Reputation: 8417
There's nothing wrong with using C-strings. It will however require you to write quite a bit of very low-level code, which has already been abstracted away when using String objects.
In general, a C-String is just an array of bytes (each byte corresponding to the ascii value of a character) with a null byte at the end. The actual syntax of the commands however can be a little esoteric, I recommended cplusplus.com as a reference.
Your second call to strtok needs to be called with null, rather than the subLine again:
char *c = strtok(subLine, "=");
while(c != NULL)
{
Msg("Token: %s\n", c);
c = strtok(null, "=");
}
At the moment you are tokenizing by the equals sign, so you will end up with:
PatchVersion
=1.1.1.2 ProductName
=l4d appID
=440
Don't forget that strtok consumes the input string, so subLine will be empty once your loop finishes.
I would begin by tokenizing the string by spaces to get each key-value pair. Then I would split each key-value pair into it's constituent elements and store the needed ones. The (s)scanf function can do this type of parsing very well. To read one pair into name
and value
:
char * name = new char[255];
char * value = new char[255];
sscanf(subLine, "%s=%s", name, value);
You may then use strncpy to copy the value into the appropriate location. sscanf does not consume the input, so subsequent calls will have to move the pointer of subLine to beyond the previous match (or use three %s=%s pairs in the format expression, along with three name and value variable pairs, if you know there will always be three).
The important thing is to get your application code as far away from the low level operations as is reasonable. If you do this type of manipulation often, finding or creating a library which parses files such as this might be a good (and fun) idea.
Upvotes: 2