Calculates values from a text file in C
Possible duplicate:
Parsing text in C
Let's say that I wrote a text file in this format:
key1/value1 key2/value2 akey/withavalue anotherkey/withanothervalue
I have a linked list like:
struct Node
{
char *key;
char *value;
struct Node *next;
};
to hold the values. How will I read key1 and value1? I was thinking about putting line by line into the buffer and using strtok (buffer, '/'). Will this work? What other ways might work, maybe slightly faster or less error prone? Please enter a sample code if you can!
a source to share
Since your problem is a very good candidate for optimizing memory fragmentation, here is an implementation that uses some simple magic to allocate all strings and the structure itself to one piece of memory.
When destroying a node, you only need one call free()
, node itself.
struct Node *list = NULL, **nextp = &list;
char buffer[1024];
while (fgets(buffer, sizeof buffer, file) != NULL) {
struct Node *node;
node = malloc(sizeof(struct Node) + strlen(buffer) + 1);
node->key = strtok(strcpy((char*)(node+1), buffer), "/\r\n");
node->value = strtok(NULL, "\r\n");
node->next = NULL;
*nextp = node;
nextp = &node->next;
}
Explanation:
With 20 comments and one unexplained top-down one, I believe the code needs some explanation, especially regarding the tricks used:
-
Creating a linked list:
struct Node *list = NULL, **nextp = &list; ... *nextp = node; nextp = &node->next;
This is a trick to create a linked list iteratively in a forward order without resorting to a special list header. It uses a pointer to a pointer to the next node. The pointer first
nextp
points to the list header pointer; in the first iteration, the head of the list is set through this pointer to the pointer, and thennextp
moves to the next pointer of this node. Subsequent iterations fill the next pointer to the last node. -
Single placement:
node = malloc(sizeof(struct Node) + strlen(buffer) + 1); node->key = ... strcpy((char*)(node+1), buffer) ...
We need to deal with three pointers: the node itself, the key string, and the value string. This will usually require three separate allocations (malloc, calloc, strdup ...) and hence free separate releases (free). Instead, in this case, the tree element spaces are summed in
sizeof(struct Node) + strlen(buffer) + 1
and passed to a single callmalloc
, which returns one block of memory. The beginning of this block of memory is assignednode
to the structure itself. Additional memory (strlen (buffer) +1) appears immediately after node and its address is obtained using pointer arithmetic withnode+1
. It is used to copy the entire line read from the file ("key / value \ n").Since it
malloc
is called a single time for each node, one allocation is performed. This means that you don't need to callfree(node->key)
andfree(node->value)
. It won't actually work. Only onefree(node)
will take care of freeing the structure and both lines in the same block. -
Parsing strings:
node->key = strtok(strcpy((char*)(node+1), buffer), "/\r\n"); node->value = strtok(NULL, "\r\n");
The first call
strtok
returns a pointer to the beginning of the buffer itself. It looks for "/" (optional for end-of-line markers) and splits the line there with a NUL character. Thus, "key / value \ n" is split into "key" and "value \ n" with a NUL character in between, and a pointer to the first is returned and stored innode->key
. The second callstrtok
will work on the remaining "value \ n", strip the end-of-line marker and return the pointer to the "value" that is stored innode->value
.
Hopefully this clears up all the questions about the above solution ... this is too much for a closed question. Full test code here .
a source to share
You can also use fscanf to parse input strings directly into keys and values:
char key[80], value[80];
fscanf (pFile, "%s/%s", key, value);
However, the disadvantage of this approach is that you need to pre-allocate sufficiently large buffers for keys and values (or use a temporary buffer and then copy its value to the final destination allocated with the desired size). With, strtok
you can check the length of each key and value, and then allocate a buffer of exactly the appropriate size to store it.
Update: As commenters pointed out, another (potentially more serious) flaw fscanf
is that it does not work for lines that contain spaces.
a source to share
If you don't mind if you had the key and value in the same block of memory (two lines), you can just read into the buffer, find the "/" change it to "\ 0" and point the pointer to the value just behind the '\ 0 '. Just remember to call free () on the key value only (this will free both the key and the value).
a source to share