如何在 C 程序中读取文本文件的最后一行?[复制]

How do I read the last line of a text file in a C program? [duplicate]

提问人:Emil Lang 提问时间:5/26/2022 最后编辑:Jonathan LefflerEmil Lang 更新时间:5/27/2022 访问量:314

问:

我正在尝试学习 C 语言,基本上我想做的是读取一个文件并将其放入我创建的结构中,然后稍后我将使用该结构做其他事情,但我想先完成第一部分。假设我有一个名为 captains.txt 的文本文件,内容是:

picard 95
janeway 90
pike 15

(请注意,最后一行只是“派克 15”)

所以我创建了一个程序,如下所示:

#include <stdio.h>
#include <stdlib.h> //for exit()
#include <string.h>
#include <ctype.h>

struct captain
{
    char capName[10];
    int number;

};
typedef struct captain captain;

int main()
    {

        FILE* file = fopen("captain.txt","r");
        if (file == NULL)
        {
            printf("\nerror opening file");
            exit(1);
        }

        else{
            printf("\nfile is opened");
        }

        char buffer[50];
        fgets(buffer,50,file);

        while (!feof(file))
        {
            captain c;
            sscanf(buffer, "%s %d", &c.capName, &c.number);
            printf("\nc captain is: %s %d", c.capName, c.number);
            fgets(buffer,50,file);
        }
        fclose(file);

        return 0;
}

我的控制台上的输出是

file is opened
c captain is: picard 95
c captain is: janeway 90
Process returned 0 (0x0)   execution time : 0.006 s
Press any key to continue.

因此,派克船长在太空中失踪了......几乎是字面意思,因为当我向文本文件添加新行时,它会变成这样:

picard 95
janeway 90
pike 15

(注意“Pike 15”后面的换行符)

然后我的输出变得正确。所以我知道我的程序没有考虑到文件末尾缺少换行符的问题......那么我该如何解决这个问题呢?

c 换行 符文件处理 特性

评论

0赞 Shawn 5/26/2022
(再加上您的文件没有尾随换行符,这使得它在技术上是一个无效的文本文件; 点击文件末尾查找它,因此您的循环永远不会为它读取的字符串运行)fgets()
0赞 Emil Lang 5/26/2022
嘿肖恩,你能解释一下无效文本文件是什么意思吗?假设您有一个用户只想使文本文件变为“名称值”,因此它看起来像窗口下方的 2 列。他们不会在最后一行之后再次点击返回并在那里结束。
1赞 Shawn 5/26/2022
从文本文件的 POSIX 定义:文本文件和二进制文件之间的唯一区别是文本文件的行数小于 {LINE_MAX} 字节,没有 NUL 字符,每个字符都以<换行符>结尾。该定义允许将具有单个<换行符>或完全空的文件的文件称为文本文件。如果文件以不完整的行结尾,则根据此定义,它不是严格意义上的文本文件。
0赞 Shawn 5/26/2022
大多数体面的编辑器会自动在文件末尾添加一个(如果还没有)。
2赞 Steve Summit 5/26/2022
如今,缺少最终换行符的文件很常见。(特别是,Windows 用户不关心文本文件的 POSIX 定义。大多数文本文件操作程序可以很好地处理丢失的最终换行符,而那些有问题的程序可以说是有问题的。

答:

1赞 Jonathan Leffler 5/27/2022 #1

比较这两个程序,一个(错误)使用,一个根本不使用它。第一个与问题中的代码密切相关——它忽略了 from 的返回值,这对它不利。第二个仅测试 ;它没有必要使用.feof()fgets()fgets()feof()

eof53.c

#include <stdio.H>

int main(void)
{
    char buffer[256];

    fgets(buffer, sizeof(buffer), stdin);
    while (!feof(stdin))
    {
        printf("[%s]\n", buffer);
        fgets(buffer, sizeof(buffer), stdin);
    }
    return 0;
}

eof71.c

#include <stdio.H>

int main(void)
{
    char buffer[256];

    while (fgets(buffer, sizeof(buffer), stdin) != NULL)
        printf("[%s]\n", buffer);
    return 0;
}

给定一个包含 3 个字节的数据文件——0x41 ()、0x42 ()、0x43 () 且没有换行符,我得到以下结果:abc'A''B''C'

$ eof53 < abc
$ eof71 < abc
[ABC]
$

这是在 MacOS Big Sur 11.6.6 上测试的。

请注意,在读取(仅)不完整的行时,不会报告 EOF(通过返回空指针),但根据经验,确实会报告 EOF——正确,因为文件输入已结束,即使确实返回了数据字符串(但不是一行)。fgets()feof()fgets()

正如规范问答中所解释的,while (!feof(file)) is always wrong!,使用而不是测试 I/O 函数的返回值会导致糟糕的结果。feof()