使用C语言计算ASCII文件中的行数

3

我希望能够计算ASCII文本文件中的行数。我认为最好的方法是通过计算文件中的换行符来实现:

for (int c = fgetc(fp); c != EOF; c = fgetc(fp)) {  /* Count word line endings. */
    if (c == '\n') ++lines;
}

然而,我不确定这是否适用于所有的 MS Windows 和 Linux 上的最后一行。如果我的文本文件以以下方式结束,没有明确的换行符,那么是否仍有编码存在,或者在 for 循环之后是否应该再添加额外的 ++lines;

cat
dog

那么如果文件末尾确实有一个显式的换行符怎么办?还是说我只需要通过追踪之前读取的值来测试这种情况?


1
你对自己的方法表示怀疑是正确的。由于 EOF 会覆盖上次读取的值,所以你需要将其保存在其他地方。一个只包含单个 \n 的文件是否包含一行还是两行? - Jongware
1
好的,这取决于是否实际上有换行符。如果使用记事本创建文档,则不会添加换行符,但某些编辑器可能会添加。您可以检查文档中的最后一个字符是否为换行符并相应地采取行动。 - user4520
3
在某些平台上,由于流锁定的流方案和函数调用开销,fgets 可能会更快一些。但是,如果您的行长度超过了缓冲区大小,仍然需要扫描缓冲区以查找 '\n',而处理最后一行会更加复杂。保持简单。 - chqrlie
1
问题:LENGTH+1很可疑。应将words[0]初始化为'\n',以避免对空文件计数1。最严重的问题是:超过LENGTH的行将被拆分并多次计数。简单的getc()循环更可靠,可能同样快速。 - chqrlie
2
如果您将一行定义为由换行符分隔的可选字符串,则根据该定义,任何尾随内容都不是一行。如果您以不同于此的方式定义“行”,则可能会有所不同。无论如何,请确保不要忘记考虑空行、空文件或没有换行符的文件等边缘情况。 - Ulrich Eckhardt
显示剩余5条评论
7个回答

3
如果没有换行符,就不会生成新的一行。C语言可以准确地告诉你这一点。

1
几乎正确:C语言告诉你有一个 '\n',但在Windows中文件实际包含 '\r''\n' - chqrlie
1
@chqrlie,除非您在“b”模式下打开它,否则请勿打开。 - user4520

3

文本文件始终应以换行符结束。对于没有行尾换行符的文件,没有标准的处理方式。

以下是一些工具如何处理最后一行换行符后的字符:

  • wc 不将其计为一行(因此您可以采用这种方式)
  • Vim 将文件标记为 [noeol] 并在保存文件时不包含行尾换行符
  • GNU sed 将文件视为具有最后一行换行符
  • shread 退出并返回数据,但仍会生成错误

由于行为几乎没有定义,您可以根据方便或有用的方式进行操作。


3

首先,最后一行不会有任何隐式编码的换行符。唯一有换行符的方式是产生文件的软件或人为其添加。然而,在最后一行添加换行符通常被认为是良好的实践。

对于应该报告的行数的最终答案取决于您需要遵循的软件或使用此行数的人所需遵循的惯例,以及您可以假定输入源的行为。

大多数命令行工具将用换行符终止其输出。在这种情况下,合理的答案可能是将换行符数量报告为实际行数。

另一方面,当文本编辑器显示文件时,您会看到边缘的行号(如果支持)包含最后一行的编号,无论它是否为空。这部分是告诉用户那里有一个空行,但是如果要计算边缘中显示的行数,则为文件中的换行符数量加一。有些编码人员习惯不在最后一行加上换行符(有时是由于粗心),因此在这种情况下,此惯例实际上将是正确的答案。

我不确定其他惯例是否有意义。例如,如果您选择在最后一行为空时不计算最后一行,则什么算作非空?在换行符后结束的文件?如果该行有空格怎么办?如果文件末尾有几行空行怎么办?


2
如果您要使用此方法,可以为所在行上的字母数量保留单独的计数器。如果最后的计数大于1,则知道最后一行有未计算的内容。请注意保留HTML标签。
int letters = 0

for (int c = fgetc(fp); c != EOF; c = fgetc(fp)) {  /* Count word line endings. */
    letters++; // Increase count on character

    if (c == '\n')
    {
        ++words;
        letters = 0; // Set back to 0 after new line
    }
}

if (letters > 0)
{
    ++words;
}

2
你的担忧是真实存在的,文件中的最后一行可能缺少最终的换行符。在 Linux 中,换行符是单个 '\n',而在 Windows 中是 CR LF 对,C 运行时会自动将其转换为 '\n'
你可以简化代码并处理最后一行缺少换行符的特殊情况,方法如下:
int c, last = '\n', lines = 0;

while ((c = getc(fp)) != EOF) {  /* Count word line endings. */
    if (c == '\n')
        lines += 1;
    last = c;
}
if (last != '\n')
    lines += 1;

如果您关心速度,那么使用getc而不是fgetc将有助于在平台上提高速度。在这些平台上,getc被定义为直接处理流结构的宏,并仅在缓冲区达到每BUFSIZ个字符左右时调用函数来重新填充缓冲区,除非该流是无缓冲的。


1
如何做呢: 为自己创建一个标志,以跟踪重置为 c == '\n'时后面的任何非 \n 字符。 在EOF之后,检查标志是否为真,如果是,则递增。
bool more_chars = false;
for (int c = fgetc(fp); c != EOF; c = fgetc(fp)) {  /* Count word line endings. */
            if (c == '\n') {
              more_chars = false;
              ++words;
            } else more_chars = true;
 }
 if(more_chars) words++;

-1

在这里,Windows和UNIX / Linux样式的换行符没有区别。在任何系统上,文本文件的最后一行可能有换行符,也可能没有。

如果您始终将行数加1,则在文件末尾存在换行符时(即文件“foo\n”将计为两行:“foo”和“”,)这实际上计算了文件末尾的空行。这可能是一个完全合理的解决方案,具体取决于您如何定义一行。

另一个“行”的定义是它总是以换行符结尾,即文件“foo\nbar”将仅由此定义中的一行(“foo”)组成。这种定义被wc使用。

当然,您可以跟踪换行符是否是文件中的最后一个字符,并仅在情况下将1添加到计数中。然后,“行”将被定义为以换行符结尾或在文件末尾非空白的内容,这对我来说听起来相当复杂。


1
这也会在一个完全空的文件中生成1行计数。 - David Hoelzer
1
@DavidHoelzer 是的,“文件末尾的空行”在这种特殊情况下将是唯一的一行。这有点像一个哲学问题,即空文件是否仍然有一行(或者空文件是否算作“文本文件”,因为没有文本)。=) - Arkku
没有哲学在这里:一个只有一个字节 '\n' 的文件有一行,一个空文件没有行。显然没有行意味着 lines = 0。哲学问题可能是:为什么“没有行”中有复数而不是“没有行”? - chqrlie
@chqrlie,你只是假设了一种线的定义,并认为它是唯一正确的。从程序员的角度来看,我认为线的最简单定义确实是“一行总是以换行符结束”,但这引出了一个问题,即文件“foo\nbar”(wc说是1)或“foo”(wc说是0)有多少行。 - Arkku
man wc中,关于不完整的最后一行的问题是这样记录的:一行被定义为由<newline>字符分隔的字符字符串。超出最后一个<newline>字符的字符将不包括在行计数中。 我同意OP必须在文件中的行数规范中非常精确。 - chqrlie
显示剩余3条评论

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