【问题标题】:strcmp on a line read with fgets使用 fgets 读取的行上的 strcmp
【发布时间】:2011-01-25 04:14:17
【问题描述】:

我正在尝试比较两个字符串。一个存储在文件中,另一个从用户 (stdin) 中检索。

这是一个示例程序:

int main()
{
    char targetName[50];
    fgets(targetName,50,stdin);

    char aName[] = "bob";
    printf("%d",strcmp(aName,targetName));

    return 0;
}

在此程序中,当输入为 "bob" 时,strcmp 返回值 -1。 为什么是这样?我认为他们应该是平等的。我怎样才能得到它,使他们是?

【问题讨论】:

  • 你应该避免使用strcmp。请改用strncmp,尤其是在与固定宽度字符串进行比较时。在这里使用 strncmp(aName,targetName,strlen(aName)) 应该适合你。
  • bta 的评论是错误的——如果要检查前缀而不是整个字符串,请使用 strncmp,例如。 "bobcat" 也会匹配。
  • 我这么说是因为strcmp 将继续比较,直到达到 NULL 终止符或字符串不同。如果您不能确定您的字符串将始终正确地以 NULL 结尾,strcmp 可能会引入缓冲区溢出和内存访问冲突。 strncmp 不仅仅用于读取前缀;将最终参数设置为固定长度缓冲区的大小,以确保不会溢出数组边界。
  • bta-您的原始评论说使用strlen 来计算strncmp 的大小参数。这样做不会增加strcmp 没有的额外保护。使用固定大小值(例如您的第二条评论指出的缓冲区大小)是应该使用 strncmp 的时间。

标签: c string input fgets strcmp


【解决方案1】:

因为 fgets 将换行符嵌入到变量 targetName 中。这会影响比较。

【讨论】:

    【解决方案2】:

    fgets 将换行符附加到字符串中,因此您最终会得到bob\n\0,它与bob\0 不同。

    【讨论】:

      【解决方案3】:

      多半是因为unixlike系统下输入“\n”中的行字符结束。

      【讨论】:

        【解决方案4】:

        fgets 读取直到看到换行符然后返回,因此当您在控制台中键入 bob 时,targetName 包含与“bob”不匹配的“bob\n”。 来自 fgets 文档:(加粗)

        从流中读取字符并将它们作为 C 字符串存储到 str 中,直到读取 (num-1) 个字符或到达换行符或文件结尾,以先到者为准。 换行符使 fgets 停止读取,但它被认为是有效字符,因此它包含在复制到 str 的字符串中。 在读取的字符之后会自动在 str 中附加一个空字符,以表示 C 字符串的结束。

        您需要在比较之前从 targetName 末尾删除换行符。

        int cch = strlen(targetName);
        if (cch > 1 && targetName[cch-1] == '\n')
           targetName[cch-1] = '\0';
        

        或将换行符添加到您的测试字符串中。

        char targetName[50];
        fgets(targetName,50,stdin);
        
        char aName[] = "bob\n";
        printf("%d",strcmp(aName,targetName));
        

        【讨论】:

        • 我建议使用sizeof(targetName),不要在fgets() 中硬编码。
        【解决方案5】:

        strcmp 是少数几个具有真假相反结果的函数之一...如果字符串相等,则结果为 0,而不是您想象的 1....

        if (strcmp(a, b)) {
            /* Do something here as the strings are not equal */
        } else {
            /* Strings are equal */
        }
        

        说到fgets,有可能在字符串的末尾附加了一个换行符......你需要摆脱它......

        +-+-+-+--+--+
        |b|o|b|\n|\0|
        +-+-+-+--+--+
        

        要摆脱换行符,请执行此操作。 注意事项:不要使用“strlen(aName) - 1”,因为 fgets 返回的行可能以 NUL 字符开头 - 因此缓冲区的索引变为 -1:

        aName[strcspn(aName, "\n")] = '\0';
        
        +-+-+-+--+
        |b|o|b|\0|
        +-+-+-+--+
        

        现在,strcmp 应该返回 0...

        【讨论】:

        • 谢谢,其他答案很好,但您的答案包括一个解决方案(我认为问题可能是 \n,但认为它也可能是其他问题),因为您包含了一个解决方案(以及一个不错的阵列图片大声笑),我已将您的标记为最佳。是的,现在 strcmp 可以工作了。
        • strcmp 不反转真假结果。 strcmp 不返回布尔值,它根据两个字符串相互关联的方式返回一个整数值。 “if (strcmp(a,b))” 有效,但掩盖了这一事实,因为在处理整数值时,“if”语句会将其视为“if (n != 0)”。更清晰的写法是“if (strcmp(a,b) != 0)”,它明确说明正在测试的内容。
        • @Torlack: or if (!strcmp(a,b))....不管你说什么,任何非零都是真的,零是假,因此我强调它...
        • 是的,如果它们匹配,strcmp 实际上返回 0,否则如果一个小于另一个,它将是一个负数,或者如果一个大于另一个,它会是一个正数(它做了一些奇怪的数学运算,哈哈)但它在某种程度上是倒置的,0 是“真”,1 或任何其他数字 = 假。如果您只是将返回值视为“差异量”,那就更有意义了。
        • 但是您的说法仍然不正确,因为它是相反的。如果你说的是真的,那么 "if (strcmp(a,b)!=true)" 也是正确的,但它不是(假设我们正在谈论一个正确实现的 C 或 C++ 版本布尔值,只能有真假状态)。 blogs.msdn.com/ericlippert/archive/2004/07/15/184431.aspx
        【解决方案6】:

        当用户按下 Enter 键时,fgets 会将 \n 附加到您从用户那里拉入的字符串中。您可以通过使用strcspn 或将\n 添加到您要比较的字符串的末尾来解决此问题。

        printf("Please enter put FILE_NAME (foo1, 2, or 3), ls, or exit: \n");
        fgets(temp, 8, stdin);
        temp[strcspn(temp, "\n")] = '\0';
        if(strcmp(temp, "ls") == 0 || strcmp(temp, "exit") == 0)
        

        这只是将\n 替换为\0,但如果你想偷懒,你可以这样做:

        printf("Please enter put FILE_NAME (foo1, 2, or 3), ls, or exit: \n");
        fgets(temp, 8, stdin);
        if(strcmp(temp, "ls\n") == 0 || strcmp(temp, "exit\n") == 0)
        

        但它没有那么优雅。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多