我正在尝试读取CSV逗号分隔文件,文件内容为
one,,three
读取文件的代码就是这个......
inFile.getline(line, 500);
token1 = strtok(line, ",");
token2 = strtok(NULL, ",");
token3 = strtok(NULL, ",");
if(token1 != NULL){
cout << "token1 = " << token1 << "\n";
}else{
cout << "token1 = null\n" ;
}
if(token2 != NULL){
cout << "token2 = " << token2 << "\n";
}else{
cout << "token2 = null\n" ;
}
if(token3 != NULL){
cout << "token3 = " << token3 << "\n";
}else{
cout << "token3 = null\n";
}
输出就是这个
token1 = one
token2 = three
token3 = null
而我的期望是输出应该是这样的......
token1 = one
token2 = null
token3 = three
我确实改变了来自
的陈述 if(token1 != NULL)
到
if(token1)
但它也不起作用。
查看此示例http://www.cplusplus.com/reference/cstring/strtok/后,我已更新
token2 = strtok(NULL, ",");
到
token2 = strtok(NULL, ",,");
它也不起作用
答案 0 :(得分:5)
从标准(C99,C ++ 11引用兼容性功能):
序列中的第一个调用将搜索s1指向的字符串,以查找 s2指向的当前分隔符字符串中未包含的第一个字符。
每个后续调用,使用空指针作为第一个参数的值,从保存的指针开始搜索,其行为如上所述。
这意味着,在查找第二个令牌时,它首先会跳过与分隔符字符串中的任何字符匹配的所有字符。因此,,,
被视为输入字符串中的单个分隔符。
如果您希望令牌制作者的工作方式与标准制作者的工作方式不同,那么您不得不在其他地方查找,例如下面的代码:
#include <string.h>
char *paxtok (char *str, char *seps) {
static char *tpos, *tkn, *pos = NULL;
static char savech;
// Specific actions for first and subsequent calls.
if (str != NULL) {
// First call, set pointer.
pos = str;
savech = 'x';
} else {
// Subsequent calls, check we've done first.
if (pos == NULL)
return NULL;
// Then put character back and advance.
while (*pos != '\0')
pos++;
*pos++ = savech;
}
// Detect previous end of string.
if (savech == '\0')
return NULL;
// Now we have pos pointing to first character.
// Find first separator or nul.
tpos = pos;
while (*tpos != '\0') {
tkn = strchr (seps, *tpos);
if (tkn != NULL)
break;
tpos++;
}
savech = *tpos;
*tpos = '\0';
return pos;
}
结合以下测试程序,应该为您提供所需的内容::
#include <stdio.h>
int usage (char *reason) {
fprintf (stderr, "ERROR: %s.\n", reason);
fprintf (stderr, "Usage: testprog <string> <separator>\n");
return 1;
}
int main (int argc, char *argv[]) {
if (argc != 3)
return usage ("wrong number of parameters");
printf ("Initial string is '%s'\n", argv[1]);
char *token = paxtok (argv[1], argv[2]);
while (token != NULL) {
printf ("Token is '%s'\n", token);
token = paxtok (NULL, argv[2]);
}
printf ("Final string is '%s'\n", argv[1]);
return 0;
}
它提供了一个完整的程序,以便您可以测试它,例如使用命令:
testprog ,_start,,middle_,end, _,
将使用第二个中的两个分隔符,下划线和逗号来标记第一个字符串。它的输出显示它是如何工作的,你可以看到它拾取空标记,包括在开始和结束时:
Initial string is ',_start,,middle_,end,'
Token is ''
Token is ''
Token is 'start'
Token is ''
Token is 'middle'
Token is ''
Token is 'end'
Token is ''
Final string is ',_start,,middle_,end,'
请记住,使用静态,它受到与strtok
相同的限制 - 您无法并排执行两个令牌化操作。你可以制作一个paxtok_r
镜像strtok_r
,但我会把它作为读者的练习。
答案 1 :(得分:5)
我在阅读CSV逗号分隔文件时遇到此问题。但是我们不能使用strtok()
作为解决方案来解决分隔符字符连续出现的问题。因为按照标准
序列中的第一个调用搜索字符串
s1
指向的第一个字符 not 包含在s2
指向的当前分隔符字符串中。 如果没有找到这样的字符,那么就没有令牌s1
指向的字符串和strtok
函数返回 空指针。如果找到这样的角色,那就是 开始第一个令牌。 C11§7.24.5.83
因此,对于我的情况,我使用strpbrk()
函数定义了另一个解决方案,这对您也很有用。
#include<iostream.h>
char *strtok_new(char * string, char const * delimiter){
static char *source = NULL;
char *p, *riturn = 0;
if(string != NULL) source = string;
if(source == NULL) return NULL;
if((p = strpbrk (source, delimiter)) != NULL) {
*p = 0;
riturn = source;
source = ++p;
}
return riturn;
}
int main(){
char string[] = "one,,three,";
char delimiter[] = ",";
char * p = strtok_new(string, delimiter);
while(p){
if(*p) cout << p << endl;
else cout << "No data" << endl;
p = strtok_new(NULL, delimiter);
}
system("pause");
return 0;
}
<强>输出强>
one
No data
three
希望这是你想要的输出。
答案 2 :(得分:2)
http://www.cplusplus.com/reference/cstring/strtok/说:
要确定令牌的开头和结尾,该函数首先从起始位置扫描分隔符中包含的第一个字符不(后者成为令牌的开头)。然后从分隔符中包含的第一个字符开始,从开头的分区开始扫描,这将成为令牌的端。如果找到终止空字符,扫描也会停止。
因此,当函数'扫描(...)分隔符'中包含的第一个字符而不是时,它会跳过任何分隔符字符序列。这使得它无法检测连续分隔符之间的“空标记”。您必须自己扫描输入字符串char-by-char。
答案 3 :(得分:2)
使用函数strsep
代替strtok
,该函数将多个定界符视为空标记并返回所有标记。
与strtok
不同,您不必使用第一个空参数调用strsep
。您可以这样称呼它:
char *string = "this,is,the,string,,,,you,want,to,parse";
char *token;
while (token = strsep(&string, ","))
{
printf("%s\n", token);
}
如果这使您感到紧张或引发编译器警告,则始终可以显式检查NULL:
while ((token = strsep(&string, ",") != NULL))
某些旧的编译器库没有strsep
。
答案 4 :(得分:0)
此版本经过改进且可重入:
char *strtok_new_r(char * string, char const * delimiter, char **saveptr) {
char *ptr, *riturn = 0;
if (string != NULL) {
*saveptr = string;
}
if (*saveptr == NULL) {
return NULL;
}
if ((ptr = strpbrk(*saveptr, delimiter)) != NULL) {
*ptr = 0;
riturn = *saveptr;
*saveptr = ++ptr;
}
if (!ptr) {
if (*saveptr) {
riturn = *saveptr;
*saveptr = NULL;
}
}
return riturn;
}