一文看懂 C 语言 I/O
再会吧,这宝贵的片刻和短暂的时机限制了我在情义上的真挚表示,也不能容我们畅叙衷曲,这本来是亲友重逢所应有的机缘;愿上帝赐给我们美好的未来,好让我们开怀畅谈!再一次告别;勇敢作战吧,祝你胜利!——威廉•莎士比亚《查理三世》
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);
打开“此电脑”,在上面一栏找到“查看”按钮,点进去,找到“文件后缀名”,看是否打勾,如果没有请务必打上。 ︎
一文看懂 C 语言 I/O的更多相关文章
- 一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系
我们知道,不同肤色的人外貌差别很大,而双胞胎的辨识很难.有意思的是Web服务器/Web容器/Web应用程序服务器/反向代理有点像四胞胎,在网络上经常一起出现.本文将带读者对这四个相似概念如何区分. 1 ...
- [转帖]一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系
一文看懂web服务器.应用服务器.web容器.反向代理服务器区别与联系 https://www.cnblogs.com/vipyoumay/p/7455431.html 我们知道,不同肤色的人外貌差别 ...
- [转帖] 一文看懂:"边缘计算"究竟是什么?为何潜力无限?
一文看懂:"边缘计算"究竟是什么?为何潜力无限? 转载cnbeta 云计算 雾计算 边缘计算... 知名创投调研机构CB Insights撰文详述了边缘计算的发展和应用前景 ...
- 一文看懂大数据的技术生态圈,Hadoop,hive,spark都有了
一文看懂大数据的技术生态圈,Hadoop,hive,spark都有了 转载: 大数据本身是个很宽泛的概念,Hadoop生态圈(或者泛生态圈)基本上都是为了处理超过单机尺度的数据处理而诞生的.你可以把它 ...
- 【转帖】一文看懂docker容器技术架构及其中的各个模块
一文看懂docker容器技术架构及其中的各个模块 原创 波波说运维 2019-09-29 00:01:00 https://www.toutiao.com/a6740234030798602763/ ...
- 一文看懂https如何保证数据传输的安全性的【转载、收藏】
一文看懂https如何保证数据传输的安全性的 一文看懂https如何保证数据传输的安全性的 大家都知道,在客户端与服务器数据传输的过程中,http协议的传输是不安全的,也就是一般情况下http是明 ...
- 一文看懂Stacking!(含Python代码)
一文看懂Stacking!(含Python代码) https://mp.weixin.qq.com/s/faQNTGgBZdZyyZscdhjwUQ
- Nature 为引,一文看懂个体化肿瘤疫苗前世今生
进入2017年,当红辣子鸡PD-1疗法,一路横扫多个适应症.而CAR-T治疗的“小车”在获得FDA专委会推荐后也已经走上高速路,成为免疫治疗又一里程碑事件.PD-1.CAR-T之后,下一个免疫治疗产品 ...
- 转载来自朱小厮博客的 一文看懂Kafka消息格式的演变
转载来自朱小厮博客的 一文看懂Kafka消息格式的演变 ✎摘要 对于一个成熟的消息中间件而言,消息格式不仅关系到功能维度的扩展,还牵涉到性能维度的优化.随着Kafka的迅猛发展,其消息格式也在 ...
随机推荐
- VMware Workstation批量克隆虚拟机
由于经常要用vmware创建虚拟机做一些测试,集群的测试使用连接克隆,可以节省磁盘的空间(如果不是因为穷,没人愿意向生活低头) 于是找到了这个bat脚本,做了一些修改和学习,为大家加上了一些注释,方便 ...
- Redis 源码简洁剖析 16 - 客户端
整体概述 客户端属性 套接字描述符 标志 输入缓冲区 命名及命令参数 命令的实现函数 输出缓冲区 客户端的创建与关闭 创建普通客户端 关闭普通客户端 参考链接 Redis 源码简洁剖析系列 整体概述 ...
- JAVA8学习——新的时间日期API&Java8总结
JAVA8-时间日期API java8之前用过的时间日期类. Date Calendar SimpleDateFormat 有很多致命的问题. 1.没有时区概念 2.计算麻烦,实现困难 3.类是可变的 ...
- 如何基于gRPC沟通微服务框架
本文我们来讲解一下如何使用 gRPC构建微服务,gRPC是一个开源框架,可用于构建可扩展且高性能的微服务并创建服务之间的通信. 背景 随着企业越来越多地转向微服务,对构建这些微服务的低延迟和可扩展框架 ...
- python-logging日志模块,以及封装实现
前言 是Python内置的标准模块,主要用于输出运行日志 基本语法 import logging # # logging模块默认设置的日志级别是warning,而debug和info的级别是低于war ...
- 【性能测试实战:jmeter+k8s+微服务+skywalking+efk】系列之:性能测试场景设计
说明: 本文是基于虚拟机环境配置设计的 性能测试需求 总tps≥100 每个业务的rt<500ms 持续稳定跑50万业务量 单场景 目的:找到单场景的性能问题,为容量场景提供参考,如果低于容量场 ...
- 基于Windows应用程序dll劫持的权限维持
假设我们通过某个漏洞,获取目标服务器的shell,在进行攻防的时候,为了不引起防守方(蓝队)的发现,我们要不能上来就把动作搞得很大,往往我们要对自己获取的"肉鸡"进行权限的维持,以 ...
- 内网安全之横向移动(冰蝎&&msf&&IPC$)
1.冰蝎介绍 冰蝎是一款目前比较流行的Webshell管理工具,在2021年更新的2021.4.20 v3.0 Beta 9 版本中去除了动态密钥协商机制,采用预共享密钥,载荷全程无明文.因其优秀的加 ...
- RFC2889广播时延测试——网络测试仪实操
一.简介 RFC 2889为LAN交换设备的基准测试提供了方法学,它将RFC 2544中为网络互联设备基准测试所定义的方法学扩展到了交换设备,提供了交换机转发性能(Forwarding Perform ...
- windev中使用DateFile和Query作为表格数据源的一个重要区别
表格可以使用DateFile(数据表)和Query(查询表)作为数据源,在列表页面中,因为表格一般都设置为不可编辑,所以这两者在表现使用上,区别都不大.尽管如此,我们还是需要清晰的知道,这两者本质上的 ...