【问题标题】:strtok() skips first tokenstrtok() 跳过第一个标记
【发布时间】:2014-08-24 01:04:00
【问题描述】:

似乎无法弄清楚为什么此代码不起作用。 它应该非常直截了当。 根据我的故障排除,在 while(token) 块中分配了 id 数组,但是当我去打印 while(token) 块之外的所有 char 数组时,id 数组只显示所有其他数组的内容。

int loadCatData(char* menuFile) {
   char line[MAX_READ];
   char id[ID_LEN];
   char hotCold[MIN_DESC_LEN];
   char name[MAX_NAME_LEN];
   char description[MAX_DESC_LEN];
   char delim[2] = "|";
   char lineTemp[MAX_READ];
   int count;
   FILE *mfMenu = fopen(menuFile, "r");

   while (fgets(line, sizeof(line), mfMenu)!=NULL) {
      count = 0;
      printf(line);

      strcpy(lineTemp,line);

      char* token = strtok(lineTemp, delim);

      while (token) {
         printf(token);
         if (count == 0) {
            strcpy(id, token);
         }
         if (count == 1) {
            strcpy(hotCold, token);
         }
         if (count == 2) {
            strcpy(name, token);
         }
         if (count == 3) {
            strcpy(description, token);
         }
         printf("\n");
         token = strtok(NULL, delim);
         count = count + 1;
      }
      printf(id);
      printf(hotCold);
      printf(name);
      printf(description);

   }
   fclose(mfMenu);
   return true;
}

【问题讨论】:

  • 你确定token是复制到id的数组还是不为空?
  • 无论如何,对于第二个、第三个和第四个选项,在 while 循环中执行 else if,因为否则,每次,即使“正确”的主体 if 语句已被执行,其他 if 语句也将被检查,但这不是必需的。
  • @usr: 使用else if 并不比看似冗余的if 检查快,因为任何合理的编译器都会在这两种情况下以相同的方式优化代码。
  • 好的,谢谢分享,没想到;)

标签: c strtok


【解决方案1】:

您是由strcpy 引起的缓冲区溢出错误的受害者。

发生的情况是 hotCold 数组太小而无法容纳您正在填充的数据,但 strcpy 不在乎,也不知道没有足够的空间。所以它继续将数据写入hotCold,然后用完空间,然后填满填充字节,然后填满id。不幸的是,hotCold 的终止空字节位于 id 的开头。

从使用strcpy 切换到strncpystrncat(我认为这比strncpy 更好)。如果您对我所说的内容持怀疑态度,请在末尾添加一行代码,如下所示:

assert (strlen (hotCold) < MIN_DESC_LEN);

另一种选择是id 字段被解释为特殊的printf 格式说明符。以防万一,请将printf(id) 替换为printf("%s", id)

【讨论】:

  • 谢谢!你是对的,是导致问题的hotCold。它只是一个字符,所以也许我应该尝试以另一种方式传递它..
  • @user3126322 不客气。这实际上是一个很难追查的错误,但让我知道的是你使用了strcpy,这是众所周知的这种错误。实际上,它曾一度被考虑从 C 语言中完全删除。所以正如我在回答中所说:不要使用它,将其替换为strncpy
  • 不幸的是,他们教我们在我目前正在学习的课程中使用 strcpy,这有点令人担忧,因为如果它像你说的那样过时和糟糕,那么这只是糟糕的教学......跨度>
  • @user3126322 转向你的老师并讽刺地说:“如果我们应该使用strcpy,那么为什么我们不使用gets?”。出于这个原因,gets 确实从 C 语言中删除了。此外,由于这些缓冲区溢出错误,我可以调整示例输入,以便您的程序会尝试删除硬盘驱动器上的每个文件。这被称为smashing the stack,一种非常残酷的黑客攻击方法。
  • strncpy() 也不是很热,因为目的地不一定是 '\0' 终止。使用str...() 函数,代码可以在函数执行之前确定操作数的长度,并确定缓冲区是否可能溢出。对于gets(),没有事先测试知道是否会发生溢出。
【解决方案2】:
int loadCatData(const char* menuFile) {
    char id[ID_LEN];
    char hotCold[MIN_DESC_LEN];
    char name[MAX_NAME_LEN];
    char description[MAX_DESC_LEN];
    FILE *mfMenu = fopen(menuFile, "r");

    while (fscanf(mfMenu, "%*s|%*s|%*s|%*s",
            sizeof(id), id, sizeof(hotCold), hotCold,
            sizeof(name), name, sizeof(description), description) == 4) {
        printf("%s %s %s %s\n", id, hotCold, name, description);
    }

    fclose(mfMenu);
    return true;
}

您不应该将程序外部的输入作为第一个参数传递给 printf。想象一下,如果其中一个标记是“%s”并且你说 printf(token)——这是未定义的行为,因为你没有传递第二个字符串来打印,如果幸运的话,你的程序将会崩溃。

【讨论】:

  • +1 表示“永远不要将来自程序外部的输入作为第一个参数传递给 printf” - 这是一个很好的安全漏洞。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多