奇怪的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”。

为什么这样,我怎么能修复它?

strtok()有两个问题。

  1. 它修改了它的输入字符串。
  2. 一次只能激活一组strtok()调用。

我认为你的问题是后者。 您在代码中也有缩进问题:

 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()运行,然后第二个在line2运行。 麻烦的是,第2行的循环完全破坏了对line的解释。 您不能将嵌套循环与strtok()

如果你必须使用类似strtok()东西,那么找POSIX strtok_r()或者微软的strtok_s() (但请注意, strtok_s()的C11标准附件K版本是不同的 – 请参阅你是否使用TR 24731’安全’function? )。

 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); } 

关于评论

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

在使用strtok_r()的限制范围内,上面的解决方案“有效”。 这是一个测试程序来演示:

 #include  #include  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: <> Token1: <> Token2: <> Token2: <<-la>> Token1: << mkdir lololol>> Token2: <> Token2: <> Token1: << ls -la>> Token2: <> Token2: <<-la>> $ 

替代使用strcspn()strspn()

如果您不想拆除原始字符串,则必须使用除strtok()系列之外的其他函数。 函数strcspn()strspn()是合适的; 它们是标准C(C89及更高版本)的一部分,虽然比其他一些function知名度低得多。 但他们已经完成了这项任务。

 #include  #include  #include  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  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); } 

示例输入和输出:

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

这段代码回到了更舒适的while循环,而不是需要使用do-while循环,这是一个好处。

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