【问题标题】:Why does C print an extra line when reading from a file?为什么 C 从文件中读取时会打印额外的一行?
【发布时间】:2013-11-07 16:42:35
【问题描述】:

我是 C 的新手,正在尝试学习如何读取文件。我的文件是一个简单的文件(仅用于测试),其中包含以下内容:

this file
has been
successfully read
by C!

所以我使用以下 C 代码读取文件:

#include <stdio.h>

int main() {

   char str[100];
   FILE *file = fopen("/myFile/path/test.txt", "r");

   if(file == NULL) {
      puts("This file does not exist!");
      return -1;
   }

   while(fgets(str, 100, file) != '\0') {
      puts(str);
   }

   fclose(file);

   return 0;
}

这样打印我的文本:

this file

has been

successfully read

by C!

当我编译并运行它时,我将它的输出通过管道传输到hexdump -C,并且可以在每行的末尾看到一个额外的0a

最后,为什么我需要声明一个字符数组来从文件中读取?如果我不知道每行有多少数据怎么办?

【问题讨论】:

  • 这不是读取文件的最佳方式。您可以使用fseekftell 查找整个文件的大小,然后使用fread 一次将其全部读取到动态大小的数组中。
  • 提供有用的错误信息:file = fopen( path, mode ); if( file == NULL ) { perror( path ); exit( 1 ); }。这给出了文件的名称和失败的原因。 (“this”没有帮助,假设文件不存在不正确。)
  • @WilliamPursell 谢谢你,我真的很想知道这一点,这将是我接下来要调查的事情。

标签: c linux char fopen


【解决方案1】:

fgets() 读取到换行符并将换行符保留在字符串中,puts() 总是在要打印的字符串中添加换行符。因此,在您的代码中使用时,您会得到双倍行距的输出。

使用fputs(str, stdout) 代替puts();它不添加换行符。

过时的函数gets() — 从 C 标准的 2011 版本中删除 — 读取到换行符但将其删除。 gets()puts() 对一起工作得很好,fgets()fputs() 也是如此。但是,您当然应该使用gets();这是一场等待发生的灾难。 (1988 年的第一个互联网蠕虫使用gets() 进行迁移——谷歌搜索“莫里斯互联网蠕虫”)。


在 cmets 中,inquisitor 询问:

为什么需要将行读入特定大小的char数组?

因为您需要确保不会超出可用空间。 C 不会为字符串自动分配空间。从某些角度来看,这是它的弱点之一。它也是一种优势,但它经常使语言新手感到困惑。如果您希望输入代码为一行分配足够的空间,请使用 POSIX 函数getline()

那么,在我点击'\0' 之前只读取和输出是否更好,因为我并不总是知道给定行上的字符数量?

没有。一般来说,你不会打'\0';大多数文本文件不包含任何这些。如果您不想为一行分配足够的空间,请使用:

int c;
while ((c = getchar()) != EOF)
    putchar(c);

它在用户代码中一次读取一个字符,但底层标准 I/O 包会缓冲输入,因此成本不会太高——以这种方式实现程序是完全可行的。如果您需要处理行,请为行分配足够的空间(我经常使用char buffer[4096];)或使用getline()

Charlie Burns 在评论中问:

为什么我们不经常看到建议使用 getline()?

我认为它并没有经常被提及,因为getline() 相对较新,而且不一定在任何地方都可用。它被添加到 POSIX 2008;它在 Linux 和 BSD 上可用。我不确定其他主流 Unix 变体(AIX、HP-UX、Solaris)。为自己编写并不难(我已经完成了),但是如果您需要编写可移植代码(尤其是如果“可移植”包括“微软”),那就很麻烦了。它的优点之一是它告诉你它实际读取的行有多长。

使用getline() 的示例

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    char *line = 0;
    size_t length = 0;
    char const name[] = "/myFile/path/test.txt";
    FILE *file = fopen(name, "r");

    if (file == NULL)
    {
        fprintf(stderr, "%s: failed to open file %s\n", argv[0], name);
        return -1;
    }

    while (getline(&line, &length, file) > 0) 
        fputs(str, stdout);

    free(line);
    fclose(file);

    return 0;
}

【讨论】:

  • 那么在我点击'\0' 之前只读取和输出是否更好,因为我并不总是知道给定行上的字符数量?
  • @JonathanLeffler,我很高兴看到 getline() 参考,为什么我们不经常看到 getline() 建议?
  • 我已经删除了对上述问题的三个“评论”答案;他们的材料现在是主要答案。
【解决方案2】:

fgets在逐行读取时将换行符保存在行尾。这使您可以确定是否实际读取了一行或只是您的缓冲区太小。

puts 在打印时总是添加换行符。

要么从fgets 删除换行符,要么使用printf

printf("%s", str);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-27
    • 2014-10-10
    • 1970-01-01
    • 2020-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-08
    相关资源
    最近更新 更多