阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

学习如何用 C 语言来进行文件输入输出操作

301次阅读
没有评论

共计 4352 个字符,预计需要花费 11 分钟才能阅读完成。

导读 如果你打算学习 C 语言的输入、输出,可以从 [neiqian]stdio.h[/neiqian] 包含文件开始。正如你从其名字中猜到的,该文件定义了所有的标准(“std”)的输入和输出(“io”)函数。

大多数人学习的第一个 stdio.h 的函数是打印格式化输出的 printf 函数。或者是用来打印一个字符串的 puts 函数。这些函数非常有用,可以将信息打印给用户,但是如果你想做更多的事情,则需要了解其他函数。

你可以通过编写一个常见 Linux 命令的副本来了解其中一些功能和方法。cp 命令主要用于复制文件。如果你查看 cp 的帮助手册,可以看到 cp 命令支持非常多的参数和选项。但最简单的功能,就是复制文件:

cp infile outfile

你只需使用一些读写文件的基本函数,就可以用 C 语言来自己实现 cp 命令。

一次读写一个字符

你可以使用 fgetcfputc 函数轻松地进行输入输出。这些函数一次只读写一个字符。该用法被定义在 stdio.h,并且这也很浅显易懂:fgetc 是从文件中读取一个字符,fputc 是将一个字符保存到文件中。

int fgetc(FILE *stream);
int fputc(int c, FILE *stream);

编写 cp 命令需要访问文件。在 C 语言中,你使用 fopen 函数打开一个文件,该函数需要两个参数:文件名和打开文件的模式。模式通常是从文件读取 (r) 或向文件写入(w)。打开文件的方式也有其他选项,但是对于本教程而言,仅关注于读写操作。

因此,将一个文件复制到另一个文件就变成了打开源文件和目标文件,接着,不断从第一个文件读取字符,然后将该字符写入第二个文件。fgetc 函数返回从输入文件中读取的单个字符,或者当文件完成后返回文件结束标记(EOF)。一旦读取到 EOF,你就完成了复制操作,就可以关闭两个文件。该代码如下所示:

do {ch = fgetc(infile);
    if (ch != EOF) {fputc(ch, outfile);
    }
  } while (ch != EOF);

你可以使用此循环编写自己的 cp 程序,以使用 fgetcfputc 函数一次读写一个字符。cp.c 源代码如下所示:

#include 
int
main(int argc, char **argv)
{
  FILE *infile;
  FILE *outfile;
  int ch;
  /* parse the command line */
  /* usage: cp infile outfile */
  if (argc != 3) {fprintf(stderr, "Incorrect usage\n");
    fprintf(stderr, "Usage: cp infile outfile\n");
    return 1;
  }
  /* open the input file */
  infile = fopen(argv[1], "r");
  if (infile == NULL) {fprintf(stderr, "Cannot open file for reading: %s\n", argv[1]);
    return 2;
  }
  /* open the output file */
  outfile = fopen(argv[2], "w");
  if (outfile == NULL) {fprintf(stderr, "Cannot open file for writing: %s\n", argv[2]);
    fclose(infile);
    return 3;
  }
  /* copy one file to the other */
  /* use fgetc and fputc */
  do {ch = fgetc(infile);
    if (ch != EOF) {fputc(ch, outfile);
    }
  } while (ch != EOF);
  /* done */
  fclose(infile);
  fclose(outfile);
  return 0;
}

你可以使用 gcc 来将 cp.c 文件编译成一个可执行文件:

$ gcc -Wall -o cp cp.c

-o cp 选项告诉编译器将编译后的程序保存到 cp 文件中。-Wall 选项告诉编译器提示所有可能的警告,如果你没有看到任何警告,则表示一切正常。

读写数据块

通过每次读写一个字符来实现自己的 cp 命令可以完成这项工作,但这并不是很快。在复制“日常”文件(例如文档和文本文件)时,你可能不会注意到,但是在复制大型文件或通过网络复制文件时,你才会注意到差异。每次处理一个字符需要大量的开销。

实现此 cp 命令的一种更好的方法是,读取一块的输入数据到内存中(称为缓存),然后将该数据集合写入到第二个文件。这样做的速度要快得多,因为程序可以一次读取更多的数据,这就就减少了从文件中“读取”的次数。

你可以使用 fread 函数将文件读入一个变量中。这个函数有几个参数:将数据读入的数组或内存缓冲区的指针(ptr),要读取的最小对象的大小(size),要读取对象的个数(nmemb),以及要读取的文件(stream):

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

不同的选项为更高级的文件输入和输出(例如,读取和写入具有特定数据结构的文件)提供了很大的灵活性。但是,在从一个文件读取数据并将数据写入另一个文件的简单情况下,可以使用一个由字符数组组成的缓冲区。

你可以使用 fwrite 函数将缓冲区中的数据写入到另一个文件。这使用了与 fread 函数有相似的一组选项:要从中读取数据的数组或内存缓冲区的指针,要读取的最小对象的大小,要读取对象的个数以及要写入的文件。

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

如果程序将文件读入缓冲区,然后将该缓冲区写入另一个文件,则数组(ptr)可以是固定大小的数组。例如,你可以使用长度为 200 个字符的字符数组作为缓冲区。

在该假设下,你需要更改 cp 程序中的循环,以将数据从文件读取到缓冲区中,然后将该缓冲区写入另一个文件中:

while (!feof(infile)) {buffer_length = fread(buffer, sizeof(char), 200, infile);
    fwrite(buffer, sizeof(char), buffer_length, outfile);
  }

这是更新后的 cp 程序的完整源代码,该程序现在使用缓冲区读取和写入数据:

#include 
int
main(int argc, char **argv)
{
  FILE *infile;
  FILE *outfile;
  char buffer[200];
  size_t buffer_length;
  /* parse the command line */
  /* usage: cp infile outfile */
  if (argc != 3) {fprintf(stderr, "Incorrect usage\n");
    fprintf(stderr, "Usage: cp infile outfile\n");
    return 1;
  }
  /* open the input file */
  infile = fopen(argv[1], "r");
  if (infile == NULL) {fprintf(stderr, "Cannot open file for reading: %s\n", argv[1]);
    return 2;
  }
  /* open the output file */
  outfile = fopen(argv[2], "w");
  if (outfile == NULL) {fprintf(stderr, "Cannot open file for writing: %s\n", argv[2]);
    fclose(infile);
    return 3;
  }
  /* copy one file to the other */
  /* use fread and fwrite */
while (!feof(infile)) {buffer_length = fread(buffer, sizeof(char), 200, infile);
    fwrite(buffer, sizeof(char), buffer_length, outfile);
  }
  /* done */
  fclose(infile);
  fclose(outfile);
  return 0;
}

由于你想将此程序与其他程序进行比较,因此请将此源代码另存为 cp2.c。你可以使用 gcc 编译程序:

$ gcc -Wall -o cp2 cp2.c

和之前一样,-o cp2 选项告诉编译器将编译后的程序保存到 cp2 程序文件中。-Wall 选项告诉编译器打开所有警告。如果你没有看到任何警告,则表示一切正常。

是的,这真的更快了

使用缓冲区读取和写入数据是实现此版本 cp 程序更好的方法。由于它可以一次将文件的多个数据读取到内存中,因此该程序不需要频繁读取数据。在小文件中,你可能没有注意到使用这两种方案的区别,但是如果你需要复制大文件,或者在较慢的介质(例如通过网络连接)上复制数据时,会发现明显的差距。

我使用 Linux time 命令进行了比较。此命令可以运行另一个程序,然后告诉你该程序花费了多长时间。对于我的测试,我希望了解所花费时间的差距,因此我复制了系统上的 628 MB CD-ROM 镜像文件。

我首先使用标准的 Linux 的 cp 命令复制了映像文件,以查看所需多长时间。一开始通过运行 Linux 的 cp 命令,同时我还避免使用 Linux 内置的文件缓存系统,使其不会给程序带来误导性能提升的可能性。使用 Linux cp 进行的测试,总计花费不到一秒钟的时间:

$ time cp FD13LIVE.iso tmpfile
real    0m0.040s
user    0m0.001s
sys     0m0.003s

运行我自己实现的 cp 命令版本,复制同一文件要花费更长的时间。每次读写一个字符则花了将近五秒钟来复制文件:

$ time ./cp FD13LIVE.iso tmpfile
real    0m4.823s
user    0m4.100s
sys     0m0.571s

从输入读取数据到缓冲区,然后将该缓冲区写入输出文件则要快得多。使用此方法复制文件花不到一秒钟:

$ time ./cp2 FD13LIVE.iso tmpfile
real    0m0.944s
user    0m0.224s
sys     0m0.608s

我演示的 cp 程序使用了 200 个字符大小的缓冲区。我确信如果一次将更多文件数据读入内存,该程序将运行得更快。但是,通过这种比较,即使只有 200 个字符的缓冲区,你也已经看到了性能上的巨大差异。


via: https://opensource.com/article/21/3/file-io-c

作者:Jim Hall 译者:wyxplus 校对:wxy

本文由 LCTT 原创翻译,Linux 中国 荣誉推出

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-25发表,共计4352字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19352
评论数
4
阅读量
8017751
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-2:飞牛配置RAID磁盘阵列

星哥带你玩飞牛NAS-2:飞牛配置RAID磁盘阵列

星哥带你玩飞牛 NAS-2:飞牛配置 RAID 磁盘阵列 前言 大家好,我是星哥之前星哥写了《星哥带你玩飞牛 ...
自己手撸一个AI智能体—跟创业大佬对话

自己手撸一个AI智能体—跟创业大佬对话

自己手撸一个 AI 智能体 — 跟创业大佬对话 前言 智能体(Agent)已经成为创业者和技术人绕...
多服务器管理神器 Nexterm 横空出世!NAS/Win/Linux 通吃,SSH/VNC/RDP 一站式搞定

多服务器管理神器 Nexterm 横空出世!NAS/Win/Linux 通吃,SSH/VNC/RDP 一站式搞定

多服务器管理神器 Nexterm 横空出世!NAS/Win/Linux 通吃,SSH/VNC/RDP 一站式搞...
升级自动部署更新SSL证书系统、申请godaddy的APIKEY

升级自动部署更新SSL证书系统、申请godaddy的APIKEY

升级自动部署更新 SSL 证书系统、申请 godaddy 的 APIKEY 公司之前花钱购买的 ssl 证书快...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
每天一个好玩的网站-手机博物馆-CHAZ 3D Experience

每天一个好玩的网站-手机博物馆-CHAZ 3D Experience

每天一个好玩的网站 - 手机博物馆 -CHAZ 3D Experience 一句话介绍:一个用 3D 方式重温...
颠覆 AI 开发效率!开源工具一站式管控 30+大模型ApiKey,秘钥付费+负载均衡全搞定

颠覆 AI 开发效率!开源工具一站式管控 30+大模型ApiKey,秘钥付费+负载均衡全搞定

  颠覆 AI 开发效率!开源工具一站式管控 30+ 大模型 ApiKey,秘钥付费 + 负载均衡全...
开源MoneyPrinterTurbo 利用AI大模型,一键生成高清短视频!

开源MoneyPrinterTurbo 利用AI大模型,一键生成高清短视频!

  开源 MoneyPrinterTurbo 利用 AI 大模型,一键生成高清短视频! 在短视频内容...
安装Black群晖DSM7.2系统安装教程(在Vmware虚拟机中、实体机均可)!

安装Black群晖DSM7.2系统安装教程(在Vmware虚拟机中、实体机均可)!

安装 Black 群晖 DSM7.2 系统安装教程(在 Vmware 虚拟机中、实体机均可)! 前言 大家好,...
12.2K Star 爆火!开源免费的 FileConverter:右键一键搞定音视频 / 图片 / 文档转换,告别多工具切换

12.2K Star 爆火!开源免费的 FileConverter:右键一键搞定音视频 / 图片 / 文档转换,告别多工具切换

12.2K Star 爆火!开源免费的 FileConverter:右键一键搞定音视频 / 图片 / 文档转换...