我有一个包含类似以下内容的CSV文件:
value;name;test;etc
我正在尝试使用strtok(string, ";")
来拆分文本。然而,这个文件可能包含零长度的数据,就像这样:
value;;test;etc
< p > strtok()
会跳过长度为零的数据。有没有办法可以避免strtok()
跳过这样的数据?
如果可用,一种可能的替代方案是使用BSD函数strsep()
而不是strtok()
。
从man page:
strsep()
函数旨在取代strtok()
函数。虽然为了可移植性应首选strtok()
函数(它符合ISO/IEC 9899:1990("ISO C90")),但无法处理空字段,即检测由两个相邻的分隔符字符分隔的字段或用于一次以上的单个字符串。strsep()
函数最初出现在4.4BSD中。
这里有一个简单的示例(也是从那个man page复制的):
char *token, *string, *tofree;
tofree = string = strdup("value;;test;etc");
while ((token = strsep(&string, ";")) != NULL)
printf("token=%s\n", token);
free(tofree);
输出:
token=value token= token=test token=etc
因此,空字段被正确处理。
当然,正如其他人已经说过的那样,这些简单的分词函数都不能正确处理引号内部的分隔符,所以如果这是一个问题,您应该使用一个适当的 CSV解析库。
无法使strtok()
不以这种方式运行。来自man page:
在解析字符串中,两个或多个连续的分隔符字节序列被视为单个分隔符。字符串开头或结尾处的分隔符字节将被忽略。换句话说:由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');
不行。
解释:"man strtok"中写道:被解析字符串中的两个或多个连续分隔符字符被视为单个分隔符。字符串开头或结尾处的分隔符被忽略。换句话说:strtok()返回的标记始终是非空字符串。
如果您的数据中包含在引号或任何其他“转义”中的分隔符,则可能会遇到问题。
我认为最好的解决方案是获取一个CSV解析库或编写自己的解析函数。
从最近的经验来看,strtok() 不一定会用结束字符串字符替换所有分隔符,而是用结束字符串字符替换它找到的第一个分隔符,并跳过后面的分隔符但保留它们在原位。
这意味着在名义情况下(在分隔符之前没有零长度字符串),在第一次调用 strtok() 后的每次调用 strtok() 都将返回一个指向以 \0
字符开始的字符串的指针。
在 strtok() 读取分隔符之间的零长度字符串的情况下,strtok() 将返回一个指向未被替换为 \0
的分隔符字符之后开始的字符串的指针。
以下是我找出 strtok() 是否跳过分隔符之间的零长度字符串的解决方案。
// Previous code is needed to point strtok to a string and start ingesting from it.
char * field_string = strtok(NULL, ',');
// Note that this can't be done after the first call to strtok for a given buffer, since the previous character would be outside of the string's memory space.
if (*(field_string-1) == '\0') {
// no delimiters were skipped
} else {
// one or more delimiters were skipped
}
char *ptr = strtok(line, ",");
char *arr[20]; // Your max may vary. Checking for overflows is an exercise for the reader
int i = 0;
while (ptr != NULL)
{
char *back = ptr-2; // go back 2 to check for extra tokens
// Not all deliminators are converted to nulls, so check for commas too
while ((*back == '\0' || *back == ',') && (back > line))
{
arr[i++] = back; // Some of these may be pointers to comma strings
back--;
}
arr[i++] = ptr;
ptr = strtok(NULL, ",");
}
strsep()
函数?它的用法与strtok()
非常相似,但可以正确返回空字段。 - Martin Raaa;bbb;"ddd;eee";fff
。 - Martin Rstrsep()
。 - Mauren