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

时间:2010-07-30 21:36:03

标签: c string

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

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

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

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

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

有什么建议吗?

8 个答案:

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

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以外的内容。它根本不打算做你想要的。当我需要这个时,我通常使用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;
}

答案 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;    
        }
    }
}