奇怪的strtok行为

时间:2012-08-11 04:23:42

标签: c strtok

char line[255];
char *token = NULL;
char *line2 = NULL;
char *temporaryToken = NULL;

if( scanf(" %[^\n]", line) > 0)
    token = strtok( line, ";" ); //divide the line by ;
    do
    {
        line2 = token;
        temporaryToken = strtok(line2, " ");
        do
        {
            //divide the line2 by spaces into command and args, not the question here]
            temporaryToken = strtok( NULL, " " );
        }while (temporaryToken != NULL );
        token = strtok( NULL, ";" );
    }while(token != NULL);
顺便说一下,这不是我的代码,只是一个如何设置的例子

在我的程序中,当我在第二次分割之前打印“令牌”变量时,它会打印出所有内容,直到;字符。

例如,假设stdIn接受了“ls -la; mkdir lololol; ls -la”,它将打印“ls -la”。但是,在第二次拆分后,打印“令牌”只会打印“ls”。

为什么会这样,我该怎么办呢?

2 个答案:

答案 0 :(得分:4)

strtok修改原始字符串。如果你想混合这样的电话,你需要复制或使用strtok_r

答案 1 :(得分:1)

strtok()存在两个问题。

  1. 它会修改其输入字符串。
  2. 一次只能激活一组strtok()次来电。
  3. 我认为你的问题是后者。您在代码中也有缩进问题:

    if (scanf(" %[^\n]", line) > 0)
        token = strtok( line, ";" );
    do
    {
        line2 = token;
        temporaryToken = strtok(line2, " ");
        do
        {
            //divide the line2 by spaces into command and args, not the question here]
            temporaryToken = strtok(NULL, " ");
        } while (temporaryToken != NULL);
        token = strtok( NULL, ";" );
    } while(token != NULL);
    

    您可能打算阅读:

    if (scanf(" %[^\n]", line) > 0)
    {
        token = strtok(line, ";");
        do
        {
            line2 = token;
            temporaryToken = strtok(line2, " ");
            do
            {
                //divide the line2 by spaces into command and args, not the question here]
                temporaryToken = strtok(NULL, " ");
            } while (temporaryToken != NULL);
            token = strtok(NULL, ";");
        } while (token != NULL);
    }
    

    假设这是您的意图,您仍然遇到strtok()上运行一个line,然后line2上运行第二个line2的问题。麻烦的是,line上的循环完全破坏了strtok()的解释。您不能将嵌套循环与strtok_s()一起使用。

    如果您必须使用strtok()之类的内容,请查找POSIX strtok_r()或Microsoft strtok_s()(但请注意,if (scanf(" %[^\n]", line) > 0) { char *end1; token = strtok_r(line, ";", &end1); do { char *end2; line2 = token; temporaryToken = strtok_r(line2, " ", &end2); do { //divide the line2 by spaces into command and args, not the question here] temporaryToken = strtok_r(NULL, " ", &end2); } while (temporaryToken != NULL); token = strtok_r(NULL, ";", &end1); } while (token != NULL); } 的C11标准附件K版本不同 - 见Do you use the TR 24731 'safe' functions?)。

    strtok()

    关于评论

    当您使用strtok_r()或其中一个亲戚时,输入字符串将被修改,如果您有多个分隔符,您将无法分辨出哪个分隔符。您可以使用字符串的副本,并进行比较(通常基于字符串开头的偏移量)。

    在使用#include <stdio.h> #include <string.h> int main(void) { char line[1024]; if (scanf(" %[^\n]", line) > 0) { char *end1; char *token; printf("Input: <<%s>>\n", line); token = strtok_r(line, ";", &end1); do { char *end2; char *line2 = token; char *temporaryToken; printf("Token1: <<%s>>\n", token); temporaryToken = strtok_r(line2, " ", &end2); do { printf("Token2: <<%s>>\n", temporaryToken); //divide the line2 by spaces into command and args, not the question here] temporaryToken = strtok_r(NULL, " ", &end2); } while (temporaryToken != NULL); token = strtok_r(NULL, ";", &end1); } while (token != NULL); } return 0; } 的限制范围内,上述解决方案“有效”。这是一个测试程序,用于演示:

    $ ./strtok-demo
    ls -la; mkdir lololol; ls -la
    Input: <<ls -la; mkdir lololol; ls -la>>
    Token1: <<ls -la>>
    Token2: <<ls>>
    Token2: <<-la>>
    Token1: << mkdir lololol>>
    Token2: <<mkdir>>
    Token2: <<lololol>>
    Token1: << ls -la>>
    Token2: <<ls>>
    Token2: <<-la>>
    $
    

    输入和输出示例:

    strcspn()

    使用strspn()strtok()

    的备选方案

    如果您不想拆除原始字符串,则必须使用除strcspn()系列之外的其他功能。函数strspn()#include <stdio.h> #include <stdlib.h> #include <string.h> static char *substrdup(const char *src, size_t len); int main(void) { char line[1024]; if (scanf(" %[^\n]", line) > 0) { char *start1 = line; size_t len1; printf("Input: <<%s>>\n", line); while ((len1 = strcspn(start1, ";")) != 0) { char *copy = substrdup(start1, len1); char *start2 = copy; size_t len2; printf("Token1: %zd <<%.*s>>\n", len1, (int)len1, start1); printf("Copy: <<%s>>\n", copy); start2 += strspn(start2, " "); // Skip leading white space while ((len2 = strcspn(start2, " ")) != 0) { printf("Token2: %zd <<%.*s>>\n", len2, (int)len2, start2); start2 += len2; start2 += strspn(start2, " "); } free(copy); start1 += len1; start1 += strspn(start1, ";"); } printf("Check: <<%s>>\n", line); } return 0; } #include <assert.h> static char *substrdup(const char *src, size_t len) { char *copy = malloc(len+1); assert(copy != 0); // Apalling error handling strategy memmove(copy, src, len); copy[len] = '\0'; return(copy); } 是合适的;它们是标准C(C89及更高版本)的一部分,虽然比其他一些功能知名度低得多。但是他们已经完成了这项任务。

    $ strcspn-demo
    ls -la; mkdir lololol; ls -la
    Input: <<ls -la; mkdir lololol; ls -la>>
    Token1: 140734970342872 <<>>
    Copy: <<ls -la>>
    Token2: 2 <<ls>>
    Token2: 3 <<-la>>
    Copy: << mkdir lololol>>
    Token2: 5 <<mkdir>>
    Token2: 7 <<lololol>>
    Copy: << ls -la>>
    Token2: 2 <<ls>>
    Token2: 3 <<-la>>
    Check: <<ls -la; mkdir lololol; ls -la>>
    $
    

    输入和输出示例:

    while

    此代码返回更舒适的do-while循环,而不是需要使用{{1}}循环,这是一个好处。