从C中的文本文件中解析值

时间:2010-04-18 19:37:15

标签: c file-io text-files token

  

可能重复:
  Parsing text in C

假设我已经以这种格式写入文本文件:

key1/value1
key2/value2
akey/withavalue
anotherkey/withanothervalue

我有一个链接列表,如:

struct Node
{
    char *key;
    char *value;
    struct Node *next;
};

保存值。我如何读取key1和value1?我想在缓冲区中逐行放置并使用strtok(缓冲区,'/')。那会有用吗?还有哪些其他方法可以工作,可能更快或更不容易出错?如果可以,请附上代码示例!

4 个答案:

答案 0 :(得分:8)

由于你的问题是优化内存碎片的一个很好的选择,这里有一个实现,它使用一些简单的奥术魔法将所有字符串和结构本身分配到一块内存中。

当销毁节点时,您只需要对节点本身进行一次free()的调用。

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;
}

说明:

有20个评论和一个无法解释的downvote,我认为代码需要一些解释,特别是关于所用的技巧:

  1. 建立链接列表:

    struct Node *list = NULL, **nextp = &list;
    ...
    *nextp = node;
    nextp = &node->next;
    

    这是以向前顺序迭代地创建链表的技巧,而不必特殊情况下列表的头部。它使用指向下一个节点的指针。首先,nextp指针指向列表头指针;在第一次迭代中,通过此指向指针设置列表头,然后将nextp移动到该节点的下一个指针。后续迭代填充最后一个节点的下一个指针。

  2. 单一分配:

    node = malloc(sizeof(struct Node) + strlen(buffer) + 1);
    node->key = ... strcpy((char*)(node+1), buffer) ...
    

    我们必须处理三个指针:节点本身,键字符串和值字符串。这通常需要三个单独的分配(malloc,calloc,strdup ......),因此需要免费的单独版本(免费)。相反,在这种情况下,树元素的空格在sizeof(struct Node) + strlen(buffer) + 1中求和并传递给单个malloc调用,该调用返回单个内存块。这块内存的开头被分配给结构本身node。附加内存(strlen(缓冲区)+1)紧跟在节点之后,它的地址是使用node+1的指针算法获得的。它用于复制从文件中读取的整个字符串(“key / value \ n”)。

    由于每个节点都会调用malloc一次,因此会进行单次分配。这意味着您无需致电free(node->key)free(node->value)。事实上,它根本不起作用。只需一个free(node)就可以在一个块中解除分配结构和两个字符串。

  3. 行解析:

    node->key = strtok(strcpy((char*)(node+1), buffer), "/\r\n");
    node->value = strtok(NULL, "\r\n");
    

    strtok的第一次调用将指针返回到缓冲区本身的开头。它寻找一个'/'(另外用于行尾标记)并用NUL字符打破那里的字符串。因此,“key / value \ n”在“key”和“value \ n”中被中断,其中包含NUL字符,并返回指向第一个的指针并存储在node->key中。对strtok的第二次调用将对剩余的“值\ n”起作用,去掉行尾标记并返回指向“{”的指针,该指针存储在node->value中。

  4. 我希望这可以清除所有关于上述解决方案的问题......对于一个封闭的问题来说,这太过分了。 The complete test code is here

答案 1 :(得分:1)

您还可以使用fscanf将输入行直接解析为键和值:

char key[80], value[80];
fscanf (pFile, "%s/%s", key, value);

但是,这种方法的缺点是你需要事先为键和值分配足够大的缓冲区(或者使用临时缓冲区,然后将其值复制到最终目的地,分配正确的大小)。使用strtok,您可以检查每个键和值的长度,然后分配一个大小合适的缓冲区来存储它。

更新:正如评论者指出的那样,fscanf的另一个(可能更严重的)缺点是它对包含空格的字符串不起作用。

答案 2 :(得分:0)

如果您不介意在一个内存块(两个字符串)中使用密钥和walue,您可以只读入一个缓冲区,找到'/'将其更改为'\ 0'并指向值指针就在'\ 0'字符后面。只是不要忘记只在键值上调用free()(这将释放键和值)。

答案 3 :(得分:-1)

循环直线:
1)找到'/'
2)位置'/'的分配键/值 3)填写键和值
在代码中:

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