I/O 函数/缓存和字节流、占位符、getchar(),putchar()

I/O 函数

C 语言提供了一些函数,用于与外部设备通信,称为输入输出函数,简称 I/O 函数。输入(import)指的是获取外部数据,输出(export)指的是向外部传递数据。

缓存和字节流

严格地说,输入输出函数并不是直接与外部设备通信,而是通过缓存(buffer)进行间接通信。这个小节介绍缓存是什么。

普通文件一般都保存在磁盘上面,跟 CPU 相比,磁盘读取或写入数据是一个很慢的操作。所以,程序直接读写磁盘是不可行的,可能每执行一行命令,都必须等半天。C 语言的解决方案,就是只要打开一个文件,就在内存里面为这个文件设置一个缓存区。

程序向文件写入数据时,程序先把数据放入缓存,等到缓存满了,再把里面的数据会一次性写入磁盘文件。这时,缓存区就空了,程序再把新的数据放入缓存,重复整个过程。

程序从文件读取数据时,文件先把一部分数据放到缓存里面,然后程序从缓存获取数据,等到缓存空了,磁盘文件再把新的数据放入缓存,重复整个过程。

内存的读写速度比磁盘快得多,缓存的设计减少了读写磁盘的次数,大大提高了程序的执行效率。另外,一次性移动大块数据,要比多次移动小块数据快得多。

这种读写模式,对于程序来说,就有点像水流(stream),不是一次性读取或写入所有数据,而是一个持续不断的过程。先操作一部分数据,等到缓存吞吐完这部分数据,再操作下一部分数据。这个过程就叫做字节流操作。

由于缓存读完就空了,所以字节流读取都是只能读一次,第二次就读不到了。这跟读取文件很不一样。

C 语言的输入输出函数,凡是涉及读写文件,都是属于字节流操作。输入函数从文件获取数据,操作的是输入流;输出函数向文件写入数据,操作的是输出流。

printf()

printf()是最常用的输出函数,用于屏幕输出,原型定义在头文件stdio.h,详见《基本语法》一章。

scanf()

基本用法

scanf()函数用于读取用户的键盘输入。程序运行到这个语句时,会停下来,等待用户从键盘输入。用户输入数据、按下回车键后,scanf()就会处理用户的输入,将其存入变量。它的原型定义在头文件stdio.h

scanf()的语法跟printf()类似。

scanf("%d", &i);

它的第一个参数是一个格式字符串,里面会放置占位符(与printf()的占位符基本一致),告诉编译器如何解读用户的输入,需要提取的数据是什么类型。这是因为 C 语言的数据都是有类型的,scanf()必须提前知道用户输入的数据类型,才能处理数据。它的其余参数就是存放用户输入的变量,格式字符串里面有多少个占位符,就有多少个变量。

上面示例中,scanf()的第一个参数%d,表示用户输入的应该是一个整数。%d就是一个占位符,%是占位符的标志,d表示整数。第二个参数&i表示,将用户从键盘输入的整数存入变量i

注意,变量前面必须加上&运算符(指针变量除外),因为scanf()传递的不是值,而是地址,即将变量i的地址指向用户输入的值。如果这里的变量是指针变量(比如字符串变量),那就不用加&运算符。

下面是一次将键盘输入读入多个变量的例子。

scanf("%d%d%f%f", &i, &j, &x, &y);

上面示例中,格式字符串%d%d%f%f,表示用户输入的前两个是整数,后两个是浮点数,比如1 -20 3.4 -4.0e3。这四个值依次放入ijxy四个变量。

scanf()处理数值占位符时,会自动过滤空白字符,包括空格、制表符、换行符等。所以,用户输入的数据之间,有一个或多个空格不影响scanf()解读数据。另外,用户使用回车键,将输入分成几行,也不影响解读。

1
-20
3.4
-4.0e3

上面示例中,用户分成四行输入,得到的结果与一行输入是完全一样的。每次按下回车键以后,scanf()就会开始解读,如果第一行匹配第一个占位符,那么下次按下回车键时,就会从第二个占位符开始解读。

scanf()处理用户输入的原理是,用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读。解读用户输入时,会从上一次解读遗留的第一个字符开始,直到读完缓存,或者遇到第一个不符合条件的字符为止。

int x;
float y;// 用户输入 "    -13.45e12# 0"
scanf("%d", &x);
scanf("%f", &y);

上面示例中,scanf()读取用户输入时,%d占位符会忽略起首的空格,从-处开始获取数据,读取到-13停下来,因为后面的.不属于整数的有效字符。这就是说,占位符%d会读到-13

第二次调用scanf()时,就会从上一次停止解读的地方,继续往下读取。这一次读取的首字符是.,由于对应的占位符是%f,会读取到.45e12,这是采用科学计数法的浮点数格式。后面的#不属于浮点数的有效字符,所以会停在这里。

由于scanf()可以连续处理多个占位符,所以上面的例子也可以写成下面这样。

scanf("%d%f", &x, &y);

scanf()的返回值是一个整数,表示成功读取的变量个数。如果没有读取任何项,或者匹配失败,则返回0。如果读取到文件结尾,则返回常量 EOF。

占位符

scanf()常用的占位符如下,与printf()的占位符基本一致。

  • %c:字符。
  • %d:整数。
  • %ffloat类型浮点数。
  • %lfdouble类型浮点数。
  • %Lflong double类型浮点数。
  • %s:字符串。
  • %[]:在方括号中指定一组匹配的字符(比如%[0-9]),遇到不在集合之中的字符,匹配将会停止。

上面所有占位符之中,除了%c以外,都会自动忽略起首的空白字符。%c不忽略空白字符,总是返回当前第一个字符,无论该字符是否为空格。如果要强制跳过字符前的空白字符,可以写成scanf(" %c", &ch),即%c前加上一个空格,表示跳过零个或多个空白字符。

下面要特别说一下占位符%s,它其实不能简单地等同于字符串。它的规则是,从当前第一个非空白字符开始读起,直到遇到空白字符(即空格、换行符、制表符等)为止。因为%s不会包含空白字符,所以无法用来读取多个单词,除非多个%s一起使用。这也意味着,scanf()不适合读取可能包含空格的字符串,比如书名或歌曲名。另外,scanf()遇到%s占位符,会在字符串变量末尾存储一个空字符\0

scanf()将字符串读入字符数组时,不会检测字符串是否超过了数组长度。所以,储存字符串时,很可能会超过数组的边界,导致预想不到的结果。为了防止这种情况,使用%s占位符时,应该指定读入字符串的最长长度,即写成%[m]s,其中的[m]是一个整数,表示读取字符串的最大长度,后面的字符将被丢弃。

char name[11];
scanf("%10s", name);

上面示例中,name是一个长度为11的字符数组,scanf()的占位符%10s表示最多读取用户输入的10个字符,后面的字符将被丢弃,这样就不会有数组溢出的风险了。

赋值忽略符

有时,用户的输入可能不符合预定的格式。

scanf("%d-%d-%d", &year, &month, &day);

上面示例中,如果用户输入2020-01-01,就会正确解读出年、月、日。问题是用户可能输入其他格式,比如2020/01/01,这种情况下,scanf()解析数据就会失败。

为了避免这种情况,scanf()提供了一个赋值忽略符(assignment suppression character)*。只要把*加在任何占位符的百分号后面,该占位符就不会返回值,解析后将被丢弃。

scanf("%d%*c%d%*c%d", &year, &month, &day);

上面示例中,%*c就是在占位符的百分号后面,加入了赋值忽略符*,表示这个占位符没有对应的变量,解读后不必返回。

sscanf()

sscanf()函数与scanf()很类似,不同之处是sscanf()从字符串里面,而不是从用户输入获取数据。它的原型定义在头文件stdio.h里面。

int sscanf(const char* s, const char* format, ...);

sscanf()的第一个参数是一个字符串指针,用来从其中获取数据。其他参数都与scanf()相同。

sscanf()主要用来处理其他输入函数读入的字符串,从其中提取数据。

fgets(str, sizeof(str), stdin);
sscanf(str, "%d%d", &i, &j);

上面示例中,fgets()先从标准输入获取了一行数据(fgets()的介绍详见下一章),存入字符数组str。然后,sscanf()再从字符串str里面提取两个整数,放入变量ij

sscanf()的一个好处是,它的数据来源不是流数据,所以可以反复使用,不像scanf()的数据来源是流数据,只能读取一次。

sscanf()的返回值是成功赋值的变量的数量,如果提取失败,返回常量 EOF。

getchar(),putchar()

(1)getchar()

getchar()函数返回用户从键盘输入的一个字符,使用时不带有任何参数。程序运行到这个命令就会暂停,等待用户从键盘输入,等同于使用scanf()方法读取一个字符。它的原型定义在头文件stdio.h

char ch;
ch = getchar();// 等同于
scanf("%c", &ch);

getchar()不会忽略起首的空白字符,总是返回当前读取的第一个字符,无论是否为空格。如果读取失败,返回常量 EOF,由于 EOF 通常是-1,所以返回值的类型要设为 int,而不是 char。

由于getchar()返回读取的字符,所以可以用在循环条件之中。

while (getchar() != '\n');

上面示例中,只有读到的字符等于换行符(\n),才会退出循环,常用来跳过某行。while循环的循环体没有任何语句,表示对该行不执行任何操作。

下面的例子是计算某一行的字符长度。

int len = 0;
while(getchar() != '\n')len++;

上面示例中,getchar()每读取一个字符,长度变量len就会加1,直到读取到换行符为止,这时len就是该行的字符长度。

下面的例子是跳过空格字符。

while ((ch = getchar()) == ' ');

上面示例中,结束循环后,变量ch等于第一个非空格字符。

(2)putchar()

putchar()函数将它的参数字符输出到屏幕,等同于使用printf()输出一个字符。它的原型定义在头文件stdio.h

putchar(ch);
// 等同于
printf("%c", ch);

操作成功时,putchar()返回输出的字符,否则返回常量 EOF。

(3)小结

由于getchar()putchar()这两个函数的用法,要比scanf()printf()更简单,而且通常是用宏来实现,所以要比scanf()printf()更快。如果操作单个字符,建议优先使用这两个函数。

puts()

puts()函数用于将参数字符串显示在屏幕(stdout)上,并且自动在字符串末尾添加换行符。它的原型定义在头文件stdio.h

puts("Here are some messages:");
puts("Hello World");

上面示例中,puts()在屏幕上输出两行内容。

写入成功时,puts()返回一个非负整数,否则返回常量 EOF。

gets()

gets()函数以前用于从stdin读取整行输入,现在已经被废除了,仍然放在这里介绍一下。

该函数读取用户的一行输入,不会跳过起始处的空白字符,直到遇到换行符为止。这个函数会丢弃换行符,将其余字符放入参数变量,并在这些字符的末尾添加一个空字符\0,使其成为一个字符串。

它经常与puts()配合使用。

char words[81];puts("Enter a string, please");
gets(words);

上面示例使用puts()在屏幕上输出提示,然后使用gets()获取用户的输入。

由于gets()获取的字符串,可能超过字符数组变量的最大长度,有安全风险,建议不要使用,改为使用fgets()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/30621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

创建CREATE_STAT_TABLE 统计信息表在达梦和oracle中的使用

达梦 创建CREATE_STAT_TABLE 统计信息表 PROCEDURE CREATE_STAT_TABLE ( STATOWN VARCHAR(128), STATTAB VARCHAR(128), TABLESPACE VARCHAR(128) DEFAULT NULL, GLOBAL_TEMPORARY BOOLEAN DEFAULT FALSE ); 创建普通表的对应系统表的列名字段包括以下: OWNER TABL…

C 语言的逻辑运算符

C 语言的逻辑运算符包括三种: 逻辑运算符可以将两个关系表达式连接起来. Suppose exp1 and exp2 are two simple relational expressions, such as cat > rat and debt 1000 . Then you can state the following: ■ exp1 && exp2 is true only if bo…

MySQL 8.0 事务定义和基本操作

MySQL 事务(Transaction)的四大特性:A、C、I、D A、原子性:(Atomicity) 一个事务是不可分割的最小工作单位。 执行的事务,要么全部成功,要么回滚到执行事务之前的状态。 C、一致…

通讯协议035——全网独有的OPC HDA知识一之聚合(四)平均值

本文简单介绍OPC HDA规范的基本概念,更多通信资源请登录网信智汇(wangxinzhihui.com)。 本节旨在详细说明HDA聚合的要求和性能。其目的是使HDA聚合标准化,以便HDA客户端能够可靠地预测聚合计算的结果并理解其含义。如果用户需要聚合中的自定义功能&…

计算机基础知识一

1、计算机系统组成 1.1 硬件 CPU:中央处理器、计算机核心部件、负责计算任务 内存:记忆功能、存储二进制数,内存是一个字节一个地址。 内存大小换算: 8 bits 1 Byte 1024 Bytes Bytes 1 KB , 1024 KB KB 1 …

putty如何保存session

标准版的putty是不支持保存session的,这是处于安全性的考虑。 不过用起来比较麻烦,可以通过快捷方式的办法来实现保存session。 新建一个快捷方式,指向哪里都可以。 右键 | 属性 | 目标 这里修改下: "C:\Program Files\PuTTY…

java 企业工程管理系统软件源码 自主研发 工程行业适用 em

​ 工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#…

文盘 Rust -- tokio 绑定 cpu 实践

tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢?这次我们来聊聊这个话题。 首先我们先写一段简单的多任务程序。 use tokio::runtime; pub fn main() {let rt runtime::Builder::new_mu…

【Github】Uptime Kuma:自托管监控工具的完美选择

简介: Uptime Kuma 是一款强大的自托管监控工具,通过简单的部署和配置,可以帮助你监控服务器、VPS 和其他网络服务的在线状态。相比于其他类似工具,Uptime Kuma 提供更多的灵活性和自由度。本文将介绍 Uptime Kuma 的功能、如何使…

Redux中reducer 中为什么每次都要返回新的state!!!

Redux中reducer 中为什么每次都要返回新的state!!! 最近在学习react相关的知识,学习redux的时候遇到看到一个面试题: 如果Redux没返回新的数据会怎样? 这就是要去纠结为什么编写reducer得时候为什么不允许直…

Unity悬浮显示提示内容

在编写unity时,需要实现鼠标在某一个按钮上时,就显示其子物体中对应的下拉菜单,为此编写了一个公共类,对于需要悬浮显示的控件均可挂载此类。代码如下: using UnityEngine; using UnityEngine.EventSystems; using Un…

服装行业多模态算法个性化产品定制方案 | 京东云技术团队

一、项目背景 AI赋能服装设计师,设计好看、好穿、好卖的服装 传统服装行业痛点 • 设计师无法准确捕捉市场趋势,抓住中国潮流 • 上新周期长,高库存滞销风险大 • 基本款居多,难以满足消费者个性化需求 解决方案 • GPT数据…

Python数据分析实战-dataframe指定多列去重(附源码和实现效果)

实现功能 Python数据分析实战-利用df.drop_duplicates(subset[,])对dataframe指定多列去重 实现代码 import pandas as pddata{state:[1,1,2,2,1,2,2],pop:[a,b,c,d,b,c,d]} framepd.DataFrame(data)frameframe.drop_duplicates(subset[pop,state]) print(frame) 实现效果 本…

软件测试工程师面试如何描述自动化测试是怎么实现的?

软件测试工程师面试的时候,但凡简历中有透露一点点自己会自动化测试的技能点的描述,都会被面试官问,那你结合你的测试项目说说自动化测试是怎么实现的?一到这里,很多网友,包括我的学生,也都一脸…

8月9日上课内容 nginx负载均衡

负载均衡工作当中用的很多的,也是面试会问的很重要的一个点 负载均衡:通过反向代理来实现(nginx只有反向代理才能做负载均衡) 正向代理的配置方法(用的较少) 反向代理的方式:四层代理与七层代…

vue树形展示插件和一维码插件

1.vue2-org-tree 2.样式修改: .org-tree-container {height: 100%; } .horizontal .org-tree-node-children {padding-left: 70px !important; } .horizontal .org-tree-node-children::before {width: 70px !important; } .org-tree-node-label-inner {cursor: p…

使用chatGPT-4 畅聊量子物理学

与chatGPT深入研究起源、基本概念,以及海森堡、德布罗意、薛定谔、玻尔、爱因斯坦和狄拉克如何得出他们的想法和方程。 1965 年,费曼(左)与朱利安施温格(未显示)和朝永信一郎(右)分享…

C 语言的 ctype.h 头文件

C 语言的 ctype.h 头文件包含了很多字符函数的函数原型, 可以专门用来处理一个字符, 这些函数都以一个字符作为实参. ctype.h 中的字符测试函数如表所示: 这些测试函数返回 0 或 1, 即 false 或 true. ctype.h 中的字符映射函数如表所示: 字符测试函数不会修改原始实参, 只会…

Python入门02

0目录 1.容器操作(序列操作) 2.函数 3.模块 1.容器操作(序列操作) 列表的基本操作 定义一个列表[] 访问列表(打印或者通过下标和索引) 新增元素 Append(在末尾) 指定位置新增元素 Insert 删除&…

Oracle 开发篇+Java通过DRCP访问Oracle数据库

标签:DRCP、Database Resident Connection Pooling、数据库驻留连接池释义:DRCP(全称Database Resident Connection Pooling)数据库驻留连接池(Oracle自己的数据库连接池技术) ★ Oracle开启并配置DRCP sq…