6

我有一个包含数据的 CSV 文件,例如

value;name;test;etc

我试图通过使用strtok(string, ";"). 但是,此文件可以包含零长度数据,如下所示:

value;;test;etc

strtok()跳过。有没有办法避免strtok像这样跳过零长度数据?

4

3 回答 3

8

一个可能的替代方法是使用 BSD 函数strsep()代替 (strtok()如果可用)。从手册页

strsep()功能旨在作为该功能的替代品strtok() 。虽然strtok()出于可移植性原因应该首选该功能(它符合 ISO/IEC 9899:1990(“ISO C90”)),但它无法处理空字段,即检测由两个相邻分隔符分隔的字段,或用于一次不止一个字符串。该strsep()功能最早出现在 4.4BSD 中。

一个简单的示例(也从该手册页复制):

char *token, *string, *tofree;

tofree = string = strdup("value;;test;etc");
while ((token = strsep(&string, ";")) != NULL)
    printf("token=%s\n", token);

free(tofree);

输出:

令牌=值
令牌=
令牌=测试
令牌=等

因此正确处理空字段。

当然,正如其他人已经说过的,这些简单的分词器函数都不能正确处理引号内的分隔符,所以如果这是一个问题,您应该使用适当的CSV 解析库。

于 2013-09-16T12:50:44.957 回答
4

没有办法strtok()不这样做。从手册页

解析字符串中的两个或多个连续分隔符字节的序列被认为是单个分隔符。忽略字符串开头或结尾的分隔符字节。换句话说:strtok() 返回的标记总是非空字符串。

但是您可以做的是检查'\0'标记之前的字符数量,因为strtok()将所有遇到的标记替换为'\0'. 这样你就会知道有多少令牌被跳过了。来源信息

记号的这一端会自动替换为空字符,并且记号的开头由函数返回。

还有一个代码示例来说明我的意思。

char* aStr = ...;
char* ptr = NULL;

ptr = strtok (...);

char* back = ptr;
int count = -1;
do {
  back--;
  if (back <= aStr) break; // to protect against reads before aStr
  count++;
} while (*back = '\0');

(在没有 ide 或测试的情况下编写,可能是一个无效的实现,但这个想法是成立的)。

于 2013-09-16T12:12:01.110 回答
2

不,你不能。来自“man strtok”:

解析字符串中的两个或多个连续分隔符的序列被认为是单个分隔符。字符串开头或结尾的分隔符将被忽略。换句话说:strtok() 返回的标记总是非空字符串。

如果您的数据包含引号内的分隔符或任何其他“转义”,您也可能会遇到问题。

我认为最好的解决方案是获取 CSV 解析库或编写自己的解析函数。

于 2013-09-16T12:14:04.373 回答