C:使用strtok解析字符串中的空标记

C:使用strtok解析字符串中的空标记,c,string,C,String,我的应用程序生成如下所示的字符串。我需要将分隔符之间的值解析为单个值 2342|2sd45|dswer|2342||5523|||3654|Pswt 我正在使用strtok在循环中执行此操作。第五个标记是5523。但是,我还需要考虑两个分隔符之间的空值。根据我的要求,5523应该是第六个代币 token = (char *)strtok(strAccInfo, "|"); for (iLoop=1;iLoop<=106;iLoop++) { token = (

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

2342|2sd45|dswer|2342||5523|||3654|Pswt
我正在使用
strtok
在循环中执行此操作。第五个标记是5523。但是,我还需要考虑两个分隔符之间的空值。根据我的要求,5523应该是第六个代币

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

for (iLoop=1;iLoop<=106;iLoop++) { 
            token = (char *)strtok(NULL, "|");
}
token=(char*)strtok(strAccInfo,“|”);
对于(iLoop=1;iLoop
在第一次调用时,函数期望
一个C字符串作为str的参数,其
第一个字符用作
开始扫描令牌的位置。
在随后的调用中,函数
需要空指针并使用
位置正好在最后一个结束后
令牌作为的新起始位置
扫描

决定开始和结束 对于令牌,函数首先扫描 从 中未包含的第一个字符 分隔符(成为 标记的开始),然后 从本节开始扫描 第一个字符的标记 包含在分隔符中,将成为 令牌的结尾

这意味着它将跳过令牌开头的任何“|”字符。将5523作为第五个令牌,这是您已经知道的。我只是想解释一下原因(我必须自己查找)。这还表示您不会得到任何空令牌

由于您的数据是以这种方式设置的,因此您有两种可能的解决方案:
1) 查找所有出现的| |并替换为| |(在其中添加空格)

2) 做一个strstr 5次,然后找到第5个元素的开头。

用strep代替:

这是strok的一个限制。设计者考虑到了用空格分隔的标记<代码>strtok
反正也没什么作用;只需滚动您自己的解析器。.

使用的不是
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;
}

在这种情况下,我通常更喜欢
p2=strchr(p1,“|”)
循环,内部有
memcpy(s,p1,p2-p1)
。它速度快,不会破坏输入缓冲区(因此可以与
const char*
)一起使用),而且非常可移植(即使在嵌入式系统上)


它也是可重入的<代码>strtok不是。(顺便说一句:可重入与多线程无关。
strtok
已经用嵌套循环中断了。可以使用
strtok\u r
,但它不是便携的。)

下面是我现在正在使用的解决方案。感谢你们所有的回应

我正在使用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;    
        }
    }
}
charstraccinfo[1024],*p2;
int iLoop;
Action(){//此值将来自实际脚本中的wrsp调用。
lr|U保存|U字符串(“323 | 90 | 95 | 95 | null | 80 | 50 | 105 | 100 | 45”,“测试参数”);
//将参数存储到字符串中-节省内存。
strcpy(strAccInfo,lr_eval_字符串(“{test_Param}”);
//获取字符串中分隔符“|”的第一个实例
p2=(char*)strchr(strAccInfo,“|”);
//启动循环-将“最大循环”值设置为超出预期的最大值。
对于(iLoop=1;iLoop
  • 凹入的
  • 线程安全
  • 严格符合ANSI标准
  • 需要调用未使用的帮助指针 上下文
e、 g

e、 g

你的作品:) 将char*c实现为参数3

受我创建此函数的启发,它应该是线程安全的,支持空令牌,并且不会更改原始字符串

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

我测试了它的简单字符串,但还没有在生产中使用它,并且也发布了它,所以你可以看到其他人对它的看法

strtok()可以说是C标准中最糟糕的东西。你可以编写自己的解析器。因为OP只搜索一个分隔符,
strcher()
可以用来代替strpbrk()
。我做的有点不同。无论如何,谢谢。谢谢你提供的信息。希望我下次需要时能记住这一点。:-D你的第一个解决方案会把我的结果搞砸,因为字符串中有一些有效的组件会在管道之间返回一个空格。第二个解决方案可能会变得单调乏味,可能无法实现因为不同数据集的字符串可能不同。@Bash-很抱歉,我无法提供更多帮助:(哦,你帮了很多忙……信息就是我们领域的力量,对吗?我从你发布的链接中获得了一些有用的信息。谢谢!我使用了你的输入并更新了我的代码。谢谢!如果你感兴趣,我有下面的代码作为答案。谢谢,受你回答的启发,我很抱歉Patrick,但你能详细解释一下怎么做吗你的解决方案有效吗?我猜
s
是原始字符串,但是
p1
p2
是什么?在某个时候,你需要解释为什么你有全局变量而不是局部变量,为什么你没有函数的返回类型(这是非常古老的C风格)。或者,更好的方法是修复代码,使其在严格的编译器警告下干净地编译。使用
iLoop=200;
来实现
break;
是脆弱的。不清楚循环控制中为什么要使用200。哦,好吧:-)我的大部分代码都在UNIX上,现在肯定会派上用场:-))以前从未听说过。如果你在Posix机器上,你可以替换'tok=malloc(tokLenght+1);memcpy(tok,string,tokLenght);tok[tokLenght]='\0';''只需
tok=strndup(string,tokLength);
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