在C语言中读取和打印.txt文件行的最清晰方法

7

本网站有多种方法来描述如何使用各种方法打印文本文件的行:

它们似乎都是为特定的示例量身定制的。

最好有一种最清晰最简洁最容易的方法来将任何文本文件的每一行打印到屏幕上,最好附有每行代码的详细说明。

注重简洁和清晰度。


你想做什么?你想倾倒整个文件吗? - benjarobin
在UNIX上,它被称为“cat”。 - Linuxios
@benjarobin 我想要将文本文件的每一行单独打印到屏幕上。如果文件有5行,我希望以最清晰的方式简单地打开它并打印这五行。至于我的实际代码;我没有特定的用途。拥有执行这个简单概念的最佳方法将是非常棒的。 - Dlinet
4个回答

9
#include <stdio.h>

static void cat(FILE *fp)
{
    char   buffer[4096];
    size_t nbytes;
    while ((nbytes = fread(buffer, sizeof(char), sizeof(buffer), fp)) != 0)
         fwrite(buffer, sizeof(char), nbytes, stdout);
}

int main(int argc, char **argv)
{
    FILE *fp;
    const char *file;
    while ((file = *++argv) != 0)
    {
        if ((fp = fopen(file, "r")) != 0)
        {
            cat(fp);
            fclose(fp);
        }
    }
    return(0);
}
cat()函数并不是必需的,但我更喜欢使用它。主程序遍历每个命令行参数并打开指定的文件。如果成功,它将调用cat()函数以打印其内容。由于对fopen()的调用没有指定"rb",因此它被打开为文本文件。如果文件未打开,则此代码会静默忽略该问题。如果没有指定文件,则根本不会打印任何内容。 cat()函数只是简单地每次读取最多4096个字节的文本块,并将它们写入标准输出('屏幕')。当没有更多内容可读时,它就会停止。
如果您想扩展代码以在未指定文件时读取标准输入,则可以使用:
if (argc == 1)
    cat(stdin);
else
{
    ...while loop as now...
}

这就是编写cat()函数的原因之一。

此代码不直接关注换行符或任何类型的行。如果您想以正式方式逐行处理它,则可以采取几种方法:

static void cat(FILE *fp)
{
    char buffer[4096];
    while (fgets(buffer, sizeof(buffer), fp) != 0)
         fputs(buffer, stdout);
}

这将逐行读写。如果任何一行超过4095个字节,它将分两个或多个操作读取该行,并以相同数量的操作写入。请注意,此假定为文本文件,使用fread()fwrite()的版本不是这样的。在POSIX系统上,使用fread()fwrite()的版本将处理数据中带有空字节('\0')的任意二进制文件,但使用fgets()fputs()的版本不会。到目前为止,两个版本都是严格的标准C(任何版本的标准),因为它们不使用任何特定于平台的扩展;它们是代码可移植性最强的。

或者,如果您拥有POSIX 2008 getline()函数,则可以使用它,但您还需要#include <stdlib.h>(因为您最终必须释放它分配的内存):

static void cat(FILE *fp)
{
    char *buffer = 0;
    size_t buflen = 0;
    while (getline(&buffer, &buflen, fp) != -1)
         fputs(buffer, stdout);
    free(buffer);
}

这个版本也无法处理二进制数据(即其中包含空字节的数据)。当然可以升级以实现该功能:

static void cat(FILE *fp)
{
    char *buffer = 0;
    size_t buflen = 0;
    ssize_t nbytes;
    while ((nbytes = getline(&buffer, &buflen, fp)) != -1)
         fwrite(buffer, sizeof(char), nbytes, stdout);
    free(buffer);
}
< p > getline() 函数会报告它读取了多少字节(之后有一个空字节),但是 fwrite() 函数是唯一的函数,它可以将任意字节流写入给定的流中。


一个 malloc() 会导致额外的条件。此外:程序无论如何都是I/O绑定的,read_bunch + write_bunch 对于磁盘LRU来说是灾难性的。 - wildplasser
@Linuxios:我在展示的3或4个版本中的代码中没有看到限制可以复制的文件大小的任何内容。您能解释一下您认为有限制的地方吗? - Jonathan Leffler
@Dlinet:我宁愿不去剖析第三个链接;问题中的代码非常丑陋(而且孩子们正在占用我的互联网连接,真烦!)。答案也没有多大帮助;它与特定文件格式紧密相关,不像上面的代码。上面的代码中没有任何内容知道文件的内容,除了期望一个文本文件之外。在Unix上,使用fread()fwrite()的版本也可以处理任意二进制文件;其他版本假设字符串中没有嵌入空字节'\0'。代码实际上做出了很少的假设。 - Jonathan Leffler
@JonathanLeffler:好的,没问题!我会再等一段时间,希望能出现奇迹。如果没有,明天我会接受你的答案,并尽力理解它 :) - Dlinet
2
@Dlinet,这是非常优秀的C代码。Python可以用更少的行数解决这个问题,但这是因为Python比C更高级。对于像这样的简单问题,Python具有优势;但是C给有经验的开发人员完全控制完成什么以及如何完成的能力。Python可以用更少的行数解决这个问题...但Python是用C编写的!(好吧,CPython是这样的。还有其他版本的Python。但CPython最先出现并且仍然是王者。) - steveha
显示剩余9条评论

2

好的,这里是我最终完成的非常简短的解决方案。我想如果没有根本性的错误,否则它不会被建议,但我想我会在这里发布它,并希望有人能够批判它:

#include <stdio.h>
main()
{
    FILE *MyFile;
    int c;
    MyFile=fopen("C:\YourFile.txt","r");
    c = fgetc(MyFile);
    while (c!=EOF)
    {
        printf("%c",c);
        c = fgetc(MyFile);
    }
}

2
有问题的地方包括:(1)这里打印的是字符,而不是题目要求的行;(2)在使用fopen()的返回值之前,没有检查文件是否打开成功;(3)您对于main()的声明不符合C99或C2011的规范——应该是 int main(void)int main(int argc, char **argv) (或者,在紧急情况下, int main());(4)最好使用putchar()fputc()而不是使用printf()来输出单个字符;(5)更符合C语言习惯的方式是使用while((c = fgetc(MyFile)) != EOF) putchar(c);;(6)缺少fclose(MyFile) - Jonathan Leffler
1
+1 是使用 int c; 的正确且重要的做法。严格来说,由于您为 main() 编写了 C89 定义,因此应在 main() 结尾处使用 return(0);(带或不带括号)。如果您的代码符合 C99 标准,则可以不使用它,但在我看来,即使在 C99 或 C2011 代码中也最好包含它。 - Jonathan Leffler
@JonathanLeffler 谢谢您,Jonathan。这非常有见地。我会查阅更多关于不同类型/年份的C语言及其main()声明的资料! - Dlinet

1
@Dlinet,你正在尝试学习如何组织程序的一些有用课程。我不会发布代码,因为已经有一个非常出色的答案;我无法改进它。但是我想向你推荐一本书。
这本书叫做《Pascal软件工具》。语言是Pascal,而不是C,但是阅读这本书对你来说不会造成严重的困难。他们开始实现像这个例子中的简单工具(在UNIX上称为cat),然后转向更高级的内容。他们不仅教授了如何组织这种程序的重要课程,还涵盖了语言设计问题。(Pascal中存在一些真正困扰他们的问题,如果你了解C,你会意识到C没有这些问题。)
这本书现在已经绝版了,但是当我学习编写代码时,我发现它非常有价值。所谓的“左角设计”方法至今仍然对我有很大帮助。
我鼓励你在亚马逊或其他地方找到一本二手书。亚马逊有起价$0.02加$4运费的二手书。

http://www.amazon.com/Software-Tools-Pascal-Brian-Kernighan/dp/0201103427

学习本书中的程序并在C语言中实现它们将是一次很好的教育练习。任何Linux系统已经拥有更强大和完全调试过的这些程序版本,但是通过学习本书并学会编写这些内容不会浪费你的时间。

或者,您可以在计算机上安装FreePascal并使用它来运行本书中的程序。

祝你好运,并且永远享受软件开发!


+1:我从最初的《软件工具》(使用Ratfor - Rational Fortran编写)中学到了很多东西。那本书和《程序设计风格之元素》对我来说都是非常有形成性的书籍,这是许多年前的事情(30年左右,也许还有几年)。但这与直接问题有些不相关。 - Jonathan Leffler

0

如果你想要一些预先准备好的东西,在 POSIX 系统上有 cat

如果你想自己写,这是基本布局:

  1. 检查文件名、权限和路径是否有效
  2. 在循环中读取直到换行符分隔符 (\n 在 Unix 上,\r\n 在 Windows/DOS 上)
  3. 检查错误。如果有错误,打印错误并中止。
  4. 将行打印到屏幕上。
  5. 重复

关键是,没有真正特定的方法来做到这一点。只需读取,然后写入,并重复。通过一些错误检查,你就可以再次使用 cat。


谢谢你,但我想要找到的是在C编程语言中实现这个功能的最佳方法。你提供的概念框架是可行的,但不是特定于语言的。例如:我是一名Python程序员,在Python中,这个过程非常简单: f = open(filepath, "r") for line in f: print(line) f.close() - Dlinet
@Dlinet:我的观点是,没有特别好或漂亮的方法来做这件事。在C语言中,需要写很多文件代码,即使是一个简单的任务也需要很多。而在Ruby中,我只需要一行代码就可以完成,但在C语言中需要更多的代码。请看其他答案中的代码,它很长。 - Linuxios
我已经查看了其他答案。例如:我发布的第三个链接有11行,但仅适用于固定行。 - Dlinet

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