使用 while(str[n++]!='\0') 计算字符串长度

4

请忽略其中的日语。
我尝试使用以下代码统计从标准输入中输入的字符串长度。但它并没有按照预期工作:

#include <stdio.h>

int main(int argc, const char *argv[]) {
    char str[100];

    printf("文字列を入力してください:");  // Please enter a string:
    fgets(str,99,stdin);

    int n = 0;
    while (str[n++] != '\0');
    printf("文字列の長さは%dです\n", n);  // The length of the string is %d\n
    return 0; 
}

例如,如果我输入 glacious,我会得到 n=10,但是我期望的是 n=8
我知道 n++ 在执行完 str[n++] != '\0' 后会将 n 加一,而 \0 是附加在每个字符串末尾的默认字符。但是这对我来说不太合理。我知道通过在结尾添加 n-=2 可以使它符合我的预期,但我真的想了解这里发生了什么。非常感谢!

6
欢迎来到SO!我认为这是正确的,你需要对每个字符、换行符和空终止符计数。glacious\n\0 => 10。 - ggorlen
1
进一步扩展@ggorlen的评论-请记住,即使找到了nul字符,n也会增加。此外,请记住,如果有空间,fgets返回输入包括“信号”换行符。 - Adrian Mole
2
附注:对于 fgets,它会从长度中减去一来为 EOS 字符腾出空间。因此,您可以提供缓冲区的完整大小(例如 100 而不是 99)。更习惯用法:fgets(str,sizeof(str),stdin); - Craig Estey
3
@MikeChen - 你应该花些时间学习如何使用你的系统/开发环境中提供的调试器,这样你就可以逐步执行代码并自己弄清楚这类问题。我认为学会自己解决问题比让别人告诉你如何操作更有收获。 - Bob Jarvis - Слава Україні
1
如果你想要字符串的长度,不包括 '\n',你可以使用:n = 0; while (str[n] != '\n' && str[n] != '\0') n++; 在这种情况下,为了去除 '\n',你可以在退出循环后简单地使用 str[n] = 0; - David C. Rankin
显示剩余6条评论
3个回答

5

"我试图通过从stdin输入的字符串计算长度"……"我知道在最后添加n-=2可以实现我的目的,但我真的想了解在这里发生了什么。"

fgets() 的文档包括以下内容:

"……从指定的流中读取一行并将其存储到由str指向的字符串中。当读取(n-1)个字符,读取换行符或到达文件结尾时停止,以先到者为准。 "

如果没有检查函数返回值,并传递一个错误的字符串长度值,就会限制检测错误的可能性,并引入未定义行为的可能性。为了解决这些问题,请更改以下内容:

fgets(str,99,stdin); 

比如说这个:

if( fgets (str, sizeof str, stdin) != NULL ) 
{    
     ...

剖析以下内容:给定用户输入值:"glacious",在内存中str的样子是这样的:

|g|l|a|c|i|o|u|s|\n|\0|?|...|?|
 0 1 2 3 4 5 6 7 8  9 10    99 

int n = 0;
while(str[n++] != '\0');

迭代次数:

    n at start                       n at finish
  • 第1项: 当 n==0str[0] (g) 不是 \0 时,n++ 并且 n==1
  • 第2项: 当 n==1str[1] (l) 不是 \0 时,n++ 并且 n==2
  • 第3项: 当 n==2str[2] (a) 不是 \0 时,n++ 并且 n==3
  • 第4项: 当 n==3str[3] (c) 不是 \0 时,n++ 并且 n==4
  • 第5项: 当 n==4str[4] (i) 不是 \0 时,n++ 并且 n==5
  • 第6项: 当 n==5str[5] (o) 不是 \0 时,n++ 并且 n==6
  • 第7项: 当 n==6str[6] (u) 不是 \0 时,n++ 并且 n==7
  • 第8项: 当 n==7str[7] (s) 不是 \0 时,n++ 并且 n==8
  • 第9项: 当 n==8str[8] (\n) 不是 \0 时,n++ 并且 n==9
  • 第10项: 当 n==9str[9] (\0) 等于 \0 时,n++ 并且 n==10

清晰地说明了所有迭代的状态,包括对n的最终后置增量操作,使其总数达到用户输入假定为只有8个字符的10\n和最终的后置增量(对于\0)解释了对n的附加值。总之,问题在于要调整您的期望,以考虑缓冲区中的所有字符,包括您无法看到的字符。

有趣的是,计算n的值并不等同于测量str的字符串长度,其中习惯用法方法(strlen())将产生9个字符的长度。鉴于C字符串的定义,以下显示了对于每种相应的查看str的方法,假设已初始化,则会产生不同的结果:

char str[100] = {0}; 

str 的内容是:"glacious\n"//隐含了 null 终止符

//method to calculate n in discussion above
//                         //yields n == 10
int len = strlen(str);     //yields n == 9
//after using strcspn()
str[strcspn(str, "\n")] = 0;
len = strlen(str);         //yields n == 8
size_t size = sizeof str;  //yields size == 100

作为附言,如果目标是计算条目数量,并且如果可以接受替代方法,请考虑简化该方法...
替换此部分:
char str[100];

printf("文字列を入力してください:");
fgets(str,99,stdin);

int n = 0;
while(str[n++] != '\0');
printf("文字列の長さは%dです\n", n);
return 0; 

使用以下代码,当遇到\n(换行符)或EOF-1)(stdio.h中的#define)时会中断循环,从而正确计算用户输入的数量(减去换行符):

int count = 0;
printf("Please enter a string:");
int c = fgetc(stdin);
while(( c != '\n') && (c != EOF))
{
    count++; //only increments when c meets criteria
    fputc(c, stdout);
    c = fgetc(stdin);
}
printf("\n\nThe length of the string is: %d\n", count);
return 0; 

n++ 也不会排除 '\0',祝好。 - alex01011
1
我点了赞,我的意思是用 n++ 他也在计算 NULL 终止符,并祝大家新年快乐。 - alex01011
1
我没有点踩,但你关于初始化数组的评论并不像告诉OP测试fgets()的返回值那样相关。此外,C标准似乎并不保证在成功调用fgets()时目标数组中除了空终止符之外没有字节被更改。 - chqrlie
1
谢谢你,ryyker,你真的很有帮助 :) 实际上,我刚刚做了同样的事情来说服自己关于n的值(为每次迭代编写n的值和递增)。我最初认为因为这是一个非常简单的问题,也许没有人会回答它。但现在我对C语言中的字符串有了更深入的了解。 - Mike Chen

4
如果fgets()stdin中遇到换行符,它会将其写入str的末尾,然后再加上空终止符,由于你在while循环的条件表达式中使用了后增运算符,所以你也包含了空终止符在内的总数。这解释了为什么你的期望值n与实际值相差2。
考虑使用<string.h>头文件中的strcspn()函数,这样就可以计算出\n之前或者空终止符(如果没有找到)之前str的长度:
size_t n = strcspn(str, "\n");

1
fgets()函数会写入换行符... fgets()不会向流中写入任何内容 - 尝试使用CTRL-D或类似的方式关闭流 - 读取的输入不会以换行符结束。如果有换行符,fgets()只是不会去掉它。 - Andrew Henle
@AndrewHenle 我从未说过fgets()会将任何内容写入流中,我说的是_fgets()会在str末尾写入换行符_,但我更新了我的解释以帮助澄清你可能有的任何困惑。 - Patrick Roberts
@AndrewHenle:硬件方向的人可能会认为处理器将数据写入内存。因此,foo[i] = '\n'; 将一个换行符写入 foo - Eric Postpischil

2
循环语句 while(str[n++] != '\0'); 统计了由 fgets() 读取的所有字节,包括换行符空终止符,因为 n 在每次测试时都会被增加,包括最后一次评估为 false 的测试。

还要注意,fgets(str, 99, stdin); 应该改为 fgets(str, 100, stdin); 或更好地使用 if (fgets(str, sizeof str, stdin) == NULL) return 1; 来避免出现意外文件结束(空文件作为输入流进行重定向)而导致未定义的行为。

修改后的版本:

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[]) {
    char str[100];

    printf("文字列を入力してください:");  // Please enter a string:
    if (!fgets(str, sizeof str, stdin))
        return 1;

    str[strcspn(str, "\n")] = '\0';  // strip the trailing newline if any

    int n;
    for (n = 0; str[n] != '\0'; n++)
        continue;

    printf("文字列の長さは%dです\n", n);  // The length of the string is %d\n
    return 0; 
}

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