使用strtok从字符串中解析空标记

问题描述 投票:5回答:8

我的应用程序生成如下所示的字符串。我需要将分隔符之间的值解析为单个值。

2342|2sd45|dswer|2342||5523|||3654|Pswt

我正在使用strtok循环执行此操作。对于第五个标记,我得到5523.但是,我需要考虑两个分隔符||之间的空值。根据我的要求,5523应该是第六个令牌。

token = (char *)strtok(strAccInfo, "|");

for (iLoop=1;iLoop<=106;iLoop++) { 
            token = (char *)strtok(NULL, "|");
}

有什么建议?

c string
8个回答
6
投票

在那种情况下,我经常喜欢里面有p2 = strchr(p1, '|')memcpy(s, p1, p2-p1)循环。它速度快,不破坏输入缓冲区(因此它可以与const char *一起使用)并且非常便携(即使在嵌入式设备上)。

它也是可重入的; strtok不是。 (BTW:reentrant与多线程无关.strtok已经使用嵌套循环中断了。可以使用strtok_r,但它不是那么便携。)


2
投票

这是strtok的限制。设计师考虑到了以空格分隔的标记。 strtok无论如何都做不了多少;只需滚动你自己的解析器。 C FAQ has an example


2
投票

在第一次调用时,函数需要一个C字符串作为str的参数,其第一个字符用作扫描标记的起始位置。在后续调用中,函数需要一个空指针,并在最后一个标记结束后立即使用该位置作为扫描的新起始位置。

为了确定令牌的开始和结束,该函数首先从起始位置扫描未包含在分隔符中的第一个字符(它成为令牌的开头)。然后从令牌的开头开始扫描包含在分隔符中的第一个字符,这将成为令牌的结尾。

这说的是它会跳过任何'|'令牌开头的字符。使5523成为您已经知道的第五个令牌。只是想我会解释原因(我必须自己查一下)。这也表示你不会获得任何空标记。

由于您的数据是以这种方式设置的,因此您有几种可能的解决方案: 1)找到所有出现的||并用|替换| (在那里放一个空格) 2)做5次strstr并找到第5个元素的开头。


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;
}
  • 折返
  • 线程
  • 严格符合ANSI标准
  • 需要一个未使用的帮助指针来自调用上下文

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


1
投票

请考虑使用strsep:strsep reference


1
投票

使用strtok以外的东西。它根本不打算做你想要的。当我需要这个时,我通常使用strcspnstrpbrk并自己处理其余的令牌。如果你不介意像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;
}

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上,所以你可以看到别人怎么看待它


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;    
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.