再会吧,这宝贵的片刻和短暂的时机限制了我在情义上的真挚表示,也不能容我们畅叙衷曲,这本来是亲友重逢所应有的机缘;愿上帝赐给我们美好的未来,好让我们开怀畅谈!再一次告别;勇敢作战吧,祝你胜利!——威廉•莎士比亚《查理三世》

0 说在前面

当你看到这篇文章时,不妨回想一下你当初第一次用 C 语言“Hello World!”时是什么样的心情。那是你第一次成功使用神秘代码完成了和计算机的交流。尽管展示信息的黑框框让你可能不大习惯这样一种沟通方式,但这难道不也有点电影里黑客那感觉了~?

不知不觉半年过去了,你为了数据结构作业绞尽脑汁,敲下最后一个分号,鼠标轻点”编译运行“。黑色高级框框跳出来,尴尬而不失礼貌地对你说:


--------------------------------
Process exited after 4.511 seconds with return value 3221225477
请按任意键继续. . .

你质问:

”你怎么了,为什么要这样对我……呜呜~“

是啊,你认识了这个框框那么久,它早已熟悉你写 bug 的习惯,而你却摸不清它的性情。你是时候应该了解一下它了。

1 标准输入输出

”你好,我叫终端,也叫控制台,英文名是 Terminal,也叫 Console,很高兴成为你的朋友。“

”你不记得我啦?我就是你每次运行程序的时候跳出来跟你聊天的那位。请看——“

“其实我并不是你的程序本体,你的程序躲在电脑里面,是它派我来跟你说话的,”

“当你在键盘上敲敲的时候,我会帮你把你输入的字符显示出来,这样你就知道你输入的对不对了,”

“然后你一行输完,按下回车,我帮你把整行字符串都传给你的程序,你的程序就会对一行字符串进行解析,如果有 scanf 函数的话还会逐个解析出里面的数字、字符等等,”

“当你的程序算完之后,会把输出的信息告诉我,我来显示到屏幕上。”


所谓 I/O,就是 Input/Output,即输入输出。通过终端读入和显示的就是“标准输入输出”,由于终端也是从键盘获取信息,并把信息显示在屏幕上。所以:

  • 标准输入也叫键盘输入
  • 标准输出也叫屏幕输出

标准输入输出的英文是 Standard Input and Output,缩写就是“stdio”,觉不觉得眼熟hhh~


——“你用的 scanf、gets、getchar 函数都是解析标准输入的,printf、puts、putchar 都是标准输出。现在知道我是干什么的了吧”

——“哦,原来是这样。但是你好丑。”

——“???那我走”

2 输出输出重定向

终端走了——你万念俱灰,把你的代码提交给希冀的评测姬。她说:

得分0.00   最后一次提交时间:2022-03-25 19:29:50

共有测试数据:5
平均占用内存:1.401K 平均CPU时间:0.00578S 平均墙钟时间:0.00576S 测试数据 评判结果
测试数据1 运行错误
测试数据2 运行错误
测试数据3 运行错误
测试数据4 运行错误
测试数据5 运行错误

——“求求你在本地测好再交给我 OK?我每天判那么多代码很累了啦!”

——“emm……我好奇你怎么知道我们的代码对不对的,也是用终端吗?”

——“终端?那不是低级的 PC 才会用的东西?我们服务器不需要这个。I/O 重定向一下就行了”


现在你可以试试这样一个操作,写好一份 C 语言代码,里面有标准输入输出函数,然后添加两行这样的语句:

#include <stdio.h>
// 一些额外的头文件和宏定义
int main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout); // 额外添加这两句 :)
...
return 0;
}

然后在你的 C 程序的同一文件夹下新建文本文档,命名为 “a.in”(注意这一步之前要确保你的电脑显示了文件后缀[1])。然后在 “a.in” 里面写上你要输入的数据,Ctrl+S 保存。

编译运行你的代码,你会发现程序直接结束,黑框框没有其它输出了。

然后你在代码所在的文件夹里发现了一个名为 “a.out” 的文本文档,里面正是你要的答案。

当然 “a.in” 和 “a.out” 可以改成你喜欢的任何名字,文本文档对后缀不敏感,跟 “.txt” 是一样的。

当然你可以像我一样玩(用编辑器打开输入文件分屏出去,调试的时候不用每次在控制台输入,多是一件美事):


回过头来我们看看这两句是什么意思:

freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
/*
* freopen: f 表示 “file (文件)”
* re 表示 “重新”
* open 表示 “打开”
*
* "a.in" / "a.out" 表示重定向的文件名
* "r" / "w" 表示文件的打开模式:"r" 意味着“读”,"w" 意味着写
* stdin / stdout 表示被替换的 I/O 方式
* 分别是标准输入(standard input)和标准输出(standart output)
*/

翻译成人话就是:

  • 我要用“只读方式”打开文件 “a.in”,并用其替换标准输入;
  • 我要以“只写方式”打开文件 “a.out”,并用其替换标准输出。
    • 顺便说一句,只写模式 “w” 下,如果找不到文件,程序会帮你创建一个~

评测姬说:

“现在你懂了?在我拿到你的程序时,会自动帮你加上 freopen 将标准 I/O 重定向为文件 I/O,再在我的 CPU 里跑程序,跑完再对比一下你的输出和标准答案一不一样就行了。”

3 文件 I/O

此时晏老师:“多出点文件 I/O 的题,难死这帮小崽子~”

理论上来说,你会 I/O 重定向之后就可以做所有文件 I/O 的题了,大不了都用 scanf 和 gets 呗。

但是有时候让你既从标准输入读入又从文件读入~文件 I/O 也不能不会是吧。

废话了这么多,终于可以讲讲你们不大清楚的 I/O 函数的用法了:

3.1 文件指针

要熟练使用文件 I/O,要过的第一关就是文件指针,它相当于给你的文件贴一个标签,让后当你需要调用函数的时候要把文件指针作为输入变量传进去,这样才能对你的文件进行操作:

FILE * file_in = fopen("in.txt", "r");
FILE * file_out = fopen("out.txt", "w");
/*
* FILE * 是一个变量类型,代表文件的指针
*
* 后面的 file_in 和 file_out 是你自己起的变量名
*
* fopen("...", "r/w"); 是打开文件的函数,前面的文件名,后面是打开模式读或写
* 表示将一个文件以某种方式打开,返回该文件的指针
*
* 以后你就可以把 file_in 或 file_out 传进其它函数里了
*/

3.2 I/O 函数

3.2.1 输入函数:

scanf("...", ...);
fscanf(file, "...", ...); // file 是前面用 "r" 模式打开的文件指针

在以下两个条件下,这两个函数是我最推荐大家使用的。

  • 需要从输入中获取数字(直接 %d 或 %lf)
  • 需要逐词对字符串处理(不含空格)

如果题不是要求类似于“读入若干行,行内有空格,对每行输出一个balabala……”这种,真心不建议用 gets 和fgets。因为 gets 很可能会产生莫名其妙的 bug(我曾解释过),fgets 不好记也不好用。

所以比如“单词统计”等等这类题,只要不怕空格,还请选择 scanf/fscanf


printf("...", ...);
fprintf(file, "...", ...);

这俩大家应该挺熟了,后面那个 fprintf 就是把输出目标换成 “w” 模式的文件指针就行了。

介绍两个新朋友:

len = fread(str, sizeof(str[0]), MAX, file);
/*
* 这个函数的作用是从 "r" 模式的 file 文件里把整个文件一股脑读到 str 里
*
* str 是要接受的字符串,尽量开大点,一定要初始化为全 0,这个函数不保证在字符串末尾补 '\0'
* sizeof(str[0]) 实际上就是一个字符的大小,表示读的单位大小
* MAX 读的最大长度,尽量跟 str[] 的容量一样大,要大于所给数据范围
* 如果读到文件末尾还不到 MAX 则返回 str 的长度
* 如果读到 MAX 则返回 MAX
* file 文件指针
*/ fwrite(str, sizeof(str[0]), len, file);
/*
* 这个函数的作用是把 str 一股脑写进 "w" 模式的 file 里
*
* str 是要写的字符串
* sizeof(str[0]) 解释同上
* len 是想写的长度,也就是 str 的长度
* file 想写的文件指针
*/

文件加密一题中,需要读取一整段文本,这种情况下,用这两个函数是最好的选择。


剩下的不太常用我大概说一下。

gets(str);              // 读到换行就停止,读进来的字符串不含换行,可能引起神秘 bug
fgets(str, MAX, file); /* 读到换行 / 文件末尾 / 超过 MAX - 1 时停止读入
* 特性:str 中保留读到的换行并自动在末尾添加 '\0' */

上面这俩函数如果想用的话还是建议好好研究一下特性小心一点使用,挺容易出 bug 的。

ch = getchar();         // 从标准输入读入单个字符,毒瘤,别用
// 建议想用的时候用 scanf("%s", str); 读字符串来避免 bug ch = fgetc(file) // 从文件中读单个字符,注意的一点是:
// 请把 ch 定义成 int 类型,因为它读到文件末尾会返回 EOF
// 而 char 类型不能储存 -1 导致无法识别文件末尾
putchar(ch);            // 向标准输出写一个字符,等同于 printf("%c", ch);
fputc(ch, file); // 向文件写一个 ch,等同于 fprintf(file, "%c", ch);


  1. 打开“此电脑”,在上面一栏找到“查看”按钮,点进去,找到“文件后缀名”,看是否打勾,如果没有请务必打上。 ︎

一文看懂 C 语言 I/O的更多相关文章

  1. 一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系

    我们知道,不同肤色的人外貌差别很大,而双胞胎的辨识很难.有意思的是Web服务器/Web容器/Web应用程序服务器/反向代理有点像四胞胎,在网络上经常一起出现.本文将带读者对这四个相似概念如何区分. 1 ...

  2. [转帖]一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系

    一文看懂web服务器.应用服务器.web容器.反向代理服务器区别与联系 https://www.cnblogs.com/vipyoumay/p/7455431.html 我们知道,不同肤色的人外貌差别 ...

  3. [转帖] 一文看懂:"边缘计算"究竟是什么?为何潜力无限?

    一文看懂:"边缘计算"究竟是什么?为何潜力无限? 转载cnbeta   云计算 雾计算 边缘计算...   知名创投调研机构CB Insights撰文详述了边缘计算的发展和应用前景 ...

  4. 一文看懂大数据的技术生态圈,Hadoop,hive,spark都有了

    一文看懂大数据的技术生态圈,Hadoop,hive,spark都有了 转载: 大数据本身是个很宽泛的概念,Hadoop生态圈(或者泛生态圈)基本上都是为了处理超过单机尺度的数据处理而诞生的.你可以把它 ...

  5. 【转帖】一文看懂docker容器技术架构及其中的各个模块

    一文看懂docker容器技术架构及其中的各个模块 原创 波波说运维 2019-09-29 00:01:00 https://www.toutiao.com/a6740234030798602763/ ...

  6. 一文看懂https如何保证数据传输的安全性的【转载、收藏】

    一文看懂https如何保证数据传输的安全性的   一文看懂https如何保证数据传输的安全性的 大家都知道,在客户端与服务器数据传输的过程中,http协议的传输是不安全的,也就是一般情况下http是明 ...

  7. 一文看懂Stacking!(含Python代码)

    一文看懂Stacking!(含Python代码) https://mp.weixin.qq.com/s/faQNTGgBZdZyyZscdhjwUQ

  8. Nature 为引,一文看懂个体化肿瘤疫苗前世今生

    进入2017年,当红辣子鸡PD-1疗法,一路横扫多个适应症.而CAR-T治疗的“小车”在获得FDA专委会推荐后也已经走上高速路,成为免疫治疗又一里程碑事件.PD-1.CAR-T之后,下一个免疫治疗产品 ...

  9. 转载来自朱小厮博客的 一文看懂Kafka消息格式的演变

    转载来自朱小厮博客的 一文看懂Kafka消息格式的演变     ✎摘要 对于一个成熟的消息中间件而言,消息格式不仅关系到功能维度的扩展,还牵涉到性能维度的优化.随着Kafka的迅猛发展,其消息格式也在 ...

随机推荐

  1. Linux性能优化之磁盘I/O调优

    I/O指标已介绍,那么如何查看系统的这些指标呢? 一.根据工具查性能 二.根据性能找工具 三.磁盘I/O观察实例 iostat 是最常用的磁盘 I/O 性能观测工具,它提供了每个磁盘的 使用率 . I ...

  2. Nginx频繁报“500 Internal Server Error”错误

    服务器导致访问量激增,频繁报"500 Internal Server Error"错误.我查了一下nginx的错误日志(apt-get方式安装的nginx的错误日志在/var/lo ...

  3. 数据分析实际案例之:pandas在餐厅评分数据中的使用

    目录 简介 餐厅评分数据简介 分析评分数据 简介 为了更好的熟练掌握pandas在实际数据分析中的应用,今天我们再介绍一下怎么使用pandas做美国餐厅评分数据的分析. 餐厅评分数据简介 数据的来源是 ...

  4. 【论文考古】分布式优化 Communication Complexity of Convex Optimization

    J. N. Tsitsiklis and Z.-Q. Luo, "Communication complexity of convex optimization," Journal ...

  5. 命令行下Git调用IDEA的diff功能

    命令行下git diff, 有人欢喜有人厌, 本文以IDEA diff为例, 介绍如何更换Git的diff工具. IDEA diff IDEA作为一个图形化工具, 其实也提供了极少一部分命令行接口, ...

  6. RainbowCrack彩虹表破解密码hash

    实验目的 使用彩虹表破解散列值b0baee9d279d34fa1dfd71aadb908c3f 实验原理 1)彩虹表破解是利用彩虹表破解散列数据的工具. 这种方法不同于暴力破解攻击.暴力破解攻击会将密 ...

  7. RFC2889广播时延测试——网络测试仪实操

    一.简介 RFC 2889为LAN交换设备的基准测试提供了方法学,它将RFC 2544中为网络互联设备基准测试所定义的方法学扩展到了交换设备,提供了交换机转发性能(Forwarding Perform ...

  8. RFC2899广播帧转发测试——网络测试仪实操

    一.简介 RFC 2889为LAN交换设备的基准测试提供了方法学,它将RFC 2544中为网络互联设备基准测试所定义的方法学扩展到了交换设备,提供了交换机转发性能(Forwarding Perform ...

  9. go面试集锦1

    目录 1.go优缺点 2.go中常量是怎么实现 3.go的值传递和引用 4.go struct能不能比较 5.go协程线程安全吗 6.go中关键字 7.make和new区别 8.defer 9.生产者 ...

  10. Maven打包异常:Failed to execute goal org.apache.maven.plugins:maven-war-plugin:2.2:war

    出现下面异常,因为默认web.xml在  src/main/webapp  下所以才出现找不到的异常. 我的项目结构为 解决办法①:  在pom.xml里面制定 web位置即可 //先确保打包方式为w ...