我的应用程序生成如下所示的字符串。我需要将分隔符之间的值解析为单个值。
2342|2sd45|dswer|2342||5523|||3654|Pswt
我正在使用strtok
来循环执行此操作。对于第五个标记,我得到5523.但是,我还需要考虑两个分隔符||
之间的空值。根据我的要求,5523应该是第六个令牌。
token = (char *)strtok(strAccInfo, "|");
for (iLoop=1;iLoop<=106;iLoop++) {
token = (char *)strtok(NULL, "|");
}
有什么建议吗?
答案 0 :(得分:6)
在这种情况下,我经常更喜欢内置p2 = strchr(p1, '|')
的{{1}}循环。它速度快,不破坏输入缓冲区(因此它可以与memcpy(s, p1, p2-p1)
一起使用)并且非常便携(即使在嵌入式上)。
它也是可重入的; const char *
不是。 (BTW:reentrant与多线程无关。strtok
已经使用嵌套循环中断了。可以使用strtok
,但它不是可移植的。)
答案 1 :(得分:2)
在第一次调用时,函数需要 一个C字符串作为str的参数,其中 第一个字符用作 起始位置扫描令牌。 在后续调用中,该函数 期望一个空指针并使用 在最后一个结束后的位置 令牌作为新的起始位置 扫描。
确定开始和结束 一个令牌,该功能首先扫描 从起始位置 第一个字符未包含在 分隔符(成为 令牌的开头)。然后 扫描从这个开头开始 第一个字符的标记 包含在分隔符中,成为 令牌的结尾。
这说的是它会跳过任何'|'令牌开头的字符。使5523成为您已经知道的第五个令牌。只是想我会解释原因(我必须自己查一下)。这也表示你不会获得任何空标记。
由于您的数据是以这种方式设置的,因此您有几种可能的解决方案:
1)找到所有出现的||并用|替换| (在那里放一个空间)
2)做5次strstr并找到第5个元素的开头。
答案 2 :(得分:2)
这是strtok
的限制。设计师考虑到了以空格分隔的标记。无论如何,strtok
并没有做太多事情;只需滚动你自己的解析器。 C FAQ has an example。
答案 3 :(得分:2)
char *mystrtok(char **m,char *s,char c)
{
char *p=s?s:*m;
if( !*p )
return 0;
*m=strchr(p,c);
if( *m )
*(*m)++=0;
else
*m=p+strlen(p);
return p;
}
e.g。
char *p,*t,s[]="2342|2sd45|dswer|2342||5523|||3654|Pswt";
for(t=mystrtok(&p,s,'|');t;t=mystrtok(&p,0,'|'))
puts(t);
e.g。
char *p,*t,s[]="2,3,4,2|2s,d4,5|dswer|23,42||5523|||3654|Pswt";
for(t=mystrtok(&p,s,'|');t;t=mystrtok(&p,0,'|'))
{
char *p1,*t1;
for(t1=mystrtok(&p1,t,',');t1;t1=mystrtok(&p1,0,','))
puts(t1);
}
你的工作:) 将char * c实现为参数3
答案 4 :(得分:1)
请使用strsep:strsep reference
答案 5 :(得分:1)
使用strtok
以外的内容。它根本不打算做你想要的。当我需要这个时,我通常使用strcspn
或strpbrk
并自己处理其余的令牌。如果您不介意修改输入字符串,如strtok
,则应该非常简单。至少马上就这样,这样的东西好像应该有效:
// Warning: untested code. Should really use something with a less-ugly interface.
char *tokenize(char *input, char const *delim) {
static char *current; // just as ugly as strtok!
char *pos, *ret;
if (input != NULL)
current = input;
if (current == NULL)
return current;
ret = current;
pos = strpbrk(current, delim);
if (pos == NULL)
current = NULL;
else {
*pos = '\0';
current = pos+1;
}
return ret;
}
答案 6 :(得分:1)
受Patrick Schlüter answer的启发,我做了这个函数,它应该是线程安全的,并且支持空令牌,并且不会更改原始字符串
char* strTok(char** newString, char* delimiter)
{
char* string = *newString;
char* delimiterFound = (char*) 0;
int tokLenght = 0;
char* tok = (char*) 0;
if(!string) return (char*) 0;
delimiterFound = strstr(string, delimiter);
if(delimiterFound){
tokLenght = delimiterFound-string;
}else{
tokLenght = strlen(string);
}
tok = malloc(tokLenght + 1);
memcpy(tok, string, tokLenght);
tok[tokLenght] = '\0';
*newString = delimiterFound ? delimiterFound + strlen(delimiter) : (char*)0;
return tok;
}
您可以像使用它
char* input = "1,2,3,,5,";
char** inputP = &input;
char* tok;
while( (tok=strTok(inputP, ",")) ){
printf("%s\n", tok);
}
这应该输出
1
2
3
5
我测试了它的简单字符串,但尚未在生产中使用它,并且也将其发布在code review上,这样您就可以了解其他人对此有何看法
答案 7 :(得分:0)
以下是现在适合我的解决方案。感谢所有回复的人。
我正在使用LoadRunner。因此,一些不熟悉的命令,但我相信流程可以很容易理解。
char strAccInfo[1024], *p2;
int iLoop;
Action() { //This value would come from the wrsp call in the actual script.
lr_save_string("323|90||95|95|null|80|50|105|100|45","test_Param");
//Store the parameter into a string - saves memory.
strcpy(strAccInfo,lr_eval_string("{test_Param}"));
//Get the first instance of the separator "|" in the string
p2 = (char *) strchr(strAccInfo,'|');
//Start a loop - Set the max loop value to more than max expected.
for (iLoop = 1;iLoop<200;iLoop++) {
//Save parameter names in sequence.
lr_param_sprintf("Param_Name","Parameter_%d",iLoop);
//Get the first instance of the separator "|" in the string (within the loop).
p2 = (char *) strchr(strAccInfo,'|');
//Save the value for the parameters in sequence.
lr_save_var(strAccInfo,p2 - strAccInfo,0,lr_eval_string("{Param_Name}"));
//Save string after the first instance of p2, as strAccInfo - for looping.
strcpy(strAccInfo,p2+1);
//Start conditional loop for checking for last value in the string.
if (strchr(strAccInfo,'|')==NULL) {
lr_param_sprintf("Param_Name","Parameter_%d",iLoop+1);
lr_save_string(strAccInfo,lr_eval_string("{Param_Name}"));
iLoop = 200;
}
}
}