使用fgets读取的一行中的strcmp函数

10

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

这是一个示例程序:

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

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

    return 0;
}
在这个程序中,当输入是 "bob" 时,strcmp 返回一个值为 -1。为什么会这样呢?我以为它们应该相等。我该怎么做才能使它们相等呢?

1
你应该避免使用 strcmp。相反,应该使用 strncmp,特别是在比较固定宽度的字符串时。在这里,使用 strncmp(aName,targetName,strlen(aName)) 应该适合你。 - bta
1
bta的评论是错误的 - 如果你想检查一个前缀是否匹配,而不是整个字符串,请使用strncmp函数,这样"bobcat"也会匹配。 - Chris Dodd
我之所以这么说是因为strcmp会一直比较直到遇到NULL终止符或者字符串不同。如果你不能确定你的字符串总是被正确地NULL终止,strcmp可能会引起缓冲区溢出和内存访问违规。strncmp不仅仅用于读取前缀;将最后一个参数设置为你固定长度缓冲区的大小,以确保你不会溢出数组边界。 - bta
1
bta- 你最初的评论说要使用 strlen 来计算 strncmp 的大小参数。这样做并没有比 strcmp 已经具有的额外保护更多的保护作用。当使用固定大小值,例如缓冲区大小时,应该使用 strncmp,正如你第二个评论所指出的那样。 - bboe
6个回答

14

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 …


1
谢谢,其他答案都很好,但是你的包括了一个解决方案(我以为问题可能是 \n,但也可能是其他什么东西),因为你提供了一个解决方案(还有一个漂亮的数组图片lol),所以我把你的答案标记为最佳答案。是的,现在 strcmp 起作用了。 - Blackbinary
1
strcmp不会颠倒true和false的结果。strcmp不返回布尔值,而是返回一个基于两个字符串相互关系的整数值。"if (strcmp(a,b))"可以工作,但它掩盖了这个事实,因为在处理整数值时,"if"语句将把它视为"if (n != 0)"。更清晰的写法是"if (strcmp(a,b) != 0)",明确地说明正在测试什么。 - Torlack
@Torlack:或者如果(!strcmp(a,b))....无论你说什么,任何非零值都是真的,而零是假的,因此我强调它... - t0mm13b
是的,strcmp函数实际上会在匹配时返回0,否则如果一个字符串小于另一个字符串,则返回一些负数,或者如果一个字符串大于另一个字符串,则返回一些正数(它执行一些奇怪的数学运算,哈哈)。但是这种方式是反转的,0表示“真”,而1或任何其他数字则表示“假”。如果您将返回值视为“差异量”,那么它就更有意义了。 - Blackbinary
但是你的陈述仍然是不正确的,因为它是相反的。如果你所说的是真的,那么 "if (strcmp(a,b)!=true)" 也将是正确的,但它并不是(假设我们正在谈论一个具有布尔实现的版本的 C 或 C++,该布尔只能具有 true 和 false 状态)。http://blogs.msdn.com/ericlippert/archive/2004/07/15/184431.aspx - Torlack

7
fgets会读取直到遇到换行符然后返回,因此当你在控制台中输入"bob"时,targetName包含"bob\n",与"bob"不匹配。 从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() 中硬编码。 - mctylr

3
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)

但是这并不太优雅。

2

由于fgets将换行符嵌入了变量targetName中,这导致比较出现问题。


1

fgets 会将换行符附加到字符串中,因此你最终得到的是 bob\n\0 而不是 bob\0


1
主要是因为在类Unix系统下输入中的换行符“\n”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接