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!

+2


a source to share


4 answers


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 then nextp

    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 call malloc

    , which returns one block of memory. The beginning of this block of memory is assigned node

    to the structure itself. Additional memory (strlen (buffer) +1) appears immediately after node and its address is obtained using pointer arithmetic with node+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 call free(node->key)

    and free(node->value)

    . It won't actually work. Only one free(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 in node->key

    . The second call strtok

    will work on the remaining "value \ n", strip the end-of-line marker and return the pointer to the "value" that is stored in node->value

    .

Hopefully this clears up all the questions about the above solution ... this is too much for a closed question. Full test code here .

+6


a source


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.

+1


a source


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).

0


a source


loop through the lines:
1) find '/'
2) split the key / value pair at position '/'
3) enter the key and value
in the code:

char * p;
if (p = strchr (str, '/')
{
  * p ++ = 0;
  key = strdup (str);
  value = strdup (p);
}
-1


a source







All Articles