【问题标题】:Incorrect counting of number of lines行数计数不正确
【发布时间】:2020-10-25 08:49:29
【问题描述】:
#include <stdio.h>
int main() {
  int c, nl;
  while ((c = getchar()) != EOF) {
    if ( c == '\n') {
      nl++;
      printf("\n%d", nl);
    }
  }
  return 0;
}

输入:
asdsndjkasndasjldk
asdsndjkasndasjldk
asdsndjkasndasjldk
asdsndjkasndasjldk
asdsndjkasndasjldk

输出: 4

代码计算输入行数,但是,当我编译和运行代码时,它显示的数字小于实际行数。

EOF 是文件结尾...

【问题讨论】:

  • 所以在号码上加1
  • nl 永远不会被初始化。打开编译器警告(可能还有优化),你的编译器会告诉你这个。
  • @NateEldredge 虽然打开警告是一个非常好的主意,但这实际上不会产生 gcc 警告。但是,clang 确实如此。
  • @klutt:对我有用:godbolt.org/z/mgZz4P。与 gcc 一样,您需要 -Wall -O 来获取有关未初始化变量的警告。因此,我对优化发表了评论。
  • @NateEldredge 啊,我明白了。不知道我需要-O。我只用了-Wall -Wextra

标签: c while-loop counting


【解决方案1】:

这里存在语义问题。在 Unix 世界中,文本文件中的一行由换行符终止。不以换行符结尾的文件甚至不被视为文本文件。与此相反,许多 Windows 程序倾向于将换行符视为行分隔符

程序计算输入中的换行符数。如果输入是文本文件,那么它也会告诉行数。如果它是由损坏的 Windows 编辑器产生的,或者如果您在最后一个换行符之前终止输入 ,那么它就会出错。但是,它可以在 Unix 文本文件中正常工作。


这不是该程序独有的。 POSIX 实用程序wc 有一个开关-l,通常被称为计算,但它实际上也计算输入中的换行符!考虑这个例子:

% printf "abc\nabc\nabc\n" | wc -l
3
% printf "abc\nabc\nabc" | wc -l  
2

【讨论】:

  • 有趣。这意味着空文件不是文本文件。
  • @klutt 空文件是文本文件,因为 0 行中的每一行都由换行符终止。
  • @AnttiHaapala 嗯,我要在那里禁食。但是你确定吗?我的意思是,你也可以说 0 行中的每一行都没有终止。
【解决方案2】:

我意识到定义行数实际上比我想象的要复杂一些。但经过一番思考,我会用这个算法和伪代码:

no_lines = 0
while (c=read_character()) != EOF
    no_lines++
    if c != '\n'
        consume_rest_of_line()

我想到了一些案例以及我“希望”它们具有的尺寸。案例如下所示。

0 行:(很明显)

<EOF>

1 行:(很明显)

Hello<EOF>

1 行:(小技巧,感觉像 1 行,但也感觉 \n 应该会影响事情)

Hello\n<EOF>

1 行:(只是必须与空文件不同)

\n<EOF>

2 行:(只需要多出一个)

\n
\n<EOF>

2 行:(好的,我想我现在有了)

\n
Hello<EOF>

当我看到这个时,我意识到行数几乎是\n 的数量,但并不完全。 \n 只是说是时候看看是否有下一行了。任何字符,包括\n 都可以开始一行,但\n 总是结束当前行,不管它是否开始。

所以我最终得到了这个代码:

int main()
{
    int c;
    size_t no_lines = 0;
    while((c = getchar()) != EOF) {
        no_lines++;
        if(c != '\n')
            while(((c = getchar()) != EOF) && c != '\n');
    }
    printf("%zu\n", no_lines);
}

另一种表达方式是:“数一下\n的个数,如果最后读到的字符不是\n,则加一个。”

【讨论】:

  • int prev = 0;(可能更干净)和if(prev &amp;&amp; prev != '\n') - 确保空文件的计数为0?使用printf "" | ./yourexe 进行测试,看看它是否有效。
  • @DavidC.Rankin 我有点像初始化为! &lt;something&gt;,因为它传达了更多关于我的意图的信息。我可以选择除\n 之外的任何号码,这就是我想发送给读者的信息。而且我看不出检查prev 是否为零有什么好处。似乎非常不必要。
  • 你的初始化工作——!'\n' 相当于!(0xa)——也就是0,当我看到它和“任何不是'\n'”时我只是挠了挠头解释。需要if(prev &amp;&amp; prev != '\n') 是为了确保no_lines 保持0 用于空文件。目前,您报告1 行是一个空文件。
  • @DavidC.Rankin 其实好像卡在死循环里了,我删了明天再看
  • 不用担心,答案很好——您只需要稍微调整一下。请参阅Count Lines in C 了解我正在谈论的内容的简单示例 - 随意使用全部或部分。
【解决方案3】:

这里有两个问题。首先,您从未初始化过nl,因此它的值是不确定的。

其次,考虑一个只有一行的文本文件:

hello

该文件仅包含一行,但没有换行符。您需要考虑这一点(可能通过将 nl 初始化为 1)。

【讨论】:

  • 如果最后读取的字符不是 '\n',我建议在末尾加 1。
  • 您还需要确保hello&lt;EOF&gt;hello\n&lt;EOF&gt; 都包含一行。 (取决于你对“线”的定义)。
  • @MadPhysicist 你的昵称非常适合你的问题:D
  • @MadPhysicist 我到底怎么加1?
  • @NateEldredge 现在让我惊讶的是,为一个简单的文本文件定义“行数”实际上是多么棘手
猜你喜欢
  • 1970-01-01
  • 2017-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多