为什么C从文件中读取时会打印额外的行?

时间:2013-11-07 16:42:36

标签: c linux char fopen

我是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

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

2 个答案:

答案 0 :(得分:8)

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

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

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


在评论中,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;
}

答案 1 :(得分:5)

fgets在逐行阅读时将换行符保存在行尾。这样可以确定实际读取了一行,或者只是缓冲区太小了。

puts始终在打印时添加换行符。

fgets修剪换行符或使用printf

printf("%s", str);