我的应用程序生成如下所示的字符串。我需要将分隔符之间的值解析为单个值。
2342|2sd45|dswer|2342||5523|||3654|Pswt
我正在使用strtok
循环执行此操作。对于第五个标记,我得到5523.但是,我需要考虑两个分隔符||
之间的空值。根据我的要求,5523应该是第六个令牌。
token = (char *)strtok(strAccInfo, "|");
for (iLoop=1;iLoop<=106;iLoop++) {
token = (char *)strtok(NULL, "|");
}
有什么建议?
在那种情况下,我经常喜欢里面有p2 = strchr(p1, '|')
的memcpy(s, p1, p2-p1)
循环。它速度快,不破坏输入缓冲区(因此它可以与const char *
一起使用)并且非常便携(即使在嵌入式设备上)。
它也是可重入的; strtok
不是。 (BTW:reentrant与多线程无关.strtok
已经使用嵌套循环中断了。可以使用strtok_r
,但它不是那么便携。)
这是strtok
的限制。设计师考虑到了以空格分隔的标记。 strtok
无论如何都做不了多少;只需滚动你自己的解析器。 C FAQ has an example。
在第一次调用时,函数需要一个C字符串作为str的参数,其第一个字符用作扫描标记的起始位置。在后续调用中,函数需要一个空指针,并在最后一个标记结束后立即使用该位置作为扫描的新起始位置。
为了确定令牌的开始和结束,该函数首先从起始位置扫描未包含在分隔符中的第一个字符(它成为令牌的开头)。然后从令牌的开头开始扫描包含在分隔符中的第一个字符,这将成为令牌的结尾。
这说的是它会跳过任何'|'令牌开头的字符。使5523成为您已经知道的第五个令牌。只是想我会解释原因(我必须自己查一下)。这也表示你不会获得任何空标记。
由于您的数据是以这种方式设置的,因此您有几种可能的解决方案: 1)找到所有出现的||并用|替换| (在那里放一个空格) 2)做5次strstr并找到第5个元素的开头。
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;
}
EG
char *p,*t,s[]="2342|2sd45|dswer|2342||5523|||3654|Pswt";
for(t=mystrtok(&p,s,'|');t;t=mystrtok(&p,0,'|'))
puts(t);
EG
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
请考虑使用strsep:strsep reference
使用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;
}
灵感来自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上,所以你可以看到别人怎么看待它
以下是现在适合我的解决方案。感谢所有回复的人。
我正在使用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;
}
}
}