尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

C语言系列之——(7)scanf和printf使用详解

C语言系列之——(7)scanf和printf使用详解 C语言中的输入与输出一printf1.基本使用2.占位符3.转义符号4.控制输出格式4.1 限定宽度4.2 总是显示正负号4.3 限定小数位数4.4 输出部分字符串二scanf1.功能介绍2.使用2.1 基本使用2.2 高级用法a.%[ ]扫描集b.被修饰的%s占位符c.格式字符串中的分隔符c.1 分隔符的影响c.2 赋值忽略符*3.处理规则3.1 底层输入缓存工作机制3.2 不同占位符对空白字符的差异化处理规则a. 数值类占位符%d、%f、%lf等b. %c、%[ ]扫描集c. 无修饰的%s占位符4.scanf 的返回值一printf1.基本使用printf() 的作用是将文本、变量数据输出到屏幕。它名字里面的 f 代表 format格式化表示可以定制输出文本的格式。我们先看最简单的使用方式直接输出一段文字。想要输出的文字内容必须放在一对双引号当中。#includestdio.hintmain(){printf(hello world);printf(I love you);return0;}上面命令会在屏幕上输出一行文字 “hello worldI love you”可以看到虽然是用不同的printf输出内容但内容不会另起一行开始写入这表明printf() 运行结束后写入光标就停留在输出结束的地方不会自动换行。要想printf实现换行输入可以使用换行符\n#includestdio.hintmain(){printf(hello world\n);printf(I love you);return0;}//输出为//hello world//I love you2.占位符上面代码中括号内双引号包裹的内容是printf的第一个参数。这个参数是一段文本我们称之为格式字符串。这段文本除了可以直接作为输出内容外还可以包含占位符程序运行时占位符的位置会由printf后续传入的其他参数填充替代填充替代遵循两条规则:1.类型对应不同类型的数据需要搭配对应的占位符。例如十进制整型int使用%d、字符串使用%s等。2.顺序对应占位符出现的先后顺序必须和后方参数顺序一一匹配。请看示例#includestdio.hintmain(){inta1,b2;printf(hello %d%d world,a,b);return0;}这串代码输出内容为“hello 12 world”而不是“hello 21 world”这就是顺序对应靠前的占位符匹配后方靠前的参数printf 的占位符与后续参数一一对应。如果格式字符串内存在 n 个占位符那么 printf 总共需要 n1 个参数第一个参数是格式字符串后面跟随 n 个参数依次匹配每一个占位符。下面是占位符的汇总% a十六进制浮点数字母输出为小写。% A十六进制浮点数字母输出为大写。% c字符。//char% d十进制整数有符号的 10 进制整数。//int% e使用科学计数法的浮点数指数部分的 e 为小写。% E使用科学计数法的浮点数指数部分的 E 为大写。% i整数基本等同于 % d。% f小数包含 float 类型和 double 类型。//float % f double - % lf% g6 个有效数字的浮点数。整数部分一旦超过 6 位就会自动转为科学计数法指数部分的 e 为小写。% G等同于 % g唯一的区别是指数部分的 E 为大写。% hd十进制 short int 类型。% ho八进制 short int 类型。% hx十六进制 short int 类型。% huunsigned short int 类型。% ld十进制 long int 类型。% lo八进制 long int 类型。% lx十六进制 long int 类型。% luunsigned long int 类型。% lld十进制 long long int 类型。% llo八进制 long long int 类型。% llx十六进制 long long int 类型。% llo八进制 long long int 类型。% llx十六进制 long long int 类型。% lluunsigned long long int 类型。% Le科学计数法表示的 long double 类型浮点数。% Lflong double 类型浮点数。% n已输出的字符串数量。该占位符本身不输出只将值存储在指定变量之中。% o八进制整数。% p指针用来打印地址。% s字符串。% u无符号整数unsigned int。% x十六进制整数。% zdsize_t 类型。下面要特别说明 printf 中的占位符 %s它其实不能简单理解为 “字符串类型标识”。它的规则是从字符串首个字符开始输出空格、制表符这类空白字符都会正常打印直到遇见字符串末尾的结束标记 \0 为止。3.转义符号除此之外还有一个特殊写法%%也就是百分号的转义写法。在 printf 的格式字符串中单独一个%会被识别为占位符的起始符号无法直接输出百分号。如果想要在屏幕打印普通的百分号%就需要连续书写两个百分号%%。#includestdio.hintmain(){printf(通过率为 95%%);return0;}类似的如果想要输出其他特殊符号可以使用\开头的转义序列(对%的转意是个特例只能用%)#includestdio.hintmain(){printf(输出换行符\\n\n);printf(输出双引号\);return0;}//执行结果//输出换行符\n//输出双引号4.控制输出格式printf还可以定制占位符的输出格式4.1 限定宽度先看下面代码#includestdio.hintmain(){intnum123;printf(%5d,num);return0;}输出结果为上面的代码中%5d表示这个占位符的宽度至少是5若不满5位空余位会被用空格代替%nd%ns等限定宽度的占位符写法中n为写死的固定最小宽度默认是右对齐默认是用空格填补空余位。若数据本身长度超过设定宽度会原样完整输出不会截断数据。若想要左对齐可以在n前加上-如%-5d ;若想用其他符号填充空余位可在n前写上对应符号如%05d需要注意的是:1.此规则仅在右对齐时生效2.标准支持的唯一自定义填充符号为数字0其他符号会失效即填充符只有 空格 和 0 两种2.0填充只对数值类型如d、u、x、f 等有效对字符串 % s、字符 % c无论左右对齐0填充标记全程无效永远只能用空格填充空位请看下面代码#includestdio.hintmain(){intnum123;intbig123456;printf(%-05d %0-5d\n,num,num);printf(%05d\n,num);printf(%5d\n,big);return0;}输出结果为除了写死最小宽度还可以用 * 符号代替n把宽度交给后续整型参数动态传入参数匹配同样遵循顺序匹配原则请看下面代码#includestdio.hintmain(){intnum123;intleft5;intright2;printf(%0*d%0*s,left,num,right,);return0;}上面代码等效为#includestdio.hintmain(){intnum123;printf(%05d%02s,num,);return0;}输出结果为4.2 总是显示正负号默认情况下。printf并不对正数显示只会对负数显示-。若想要正数显示出可在%后加上请看下面代码#includestdio.hintmain(){intnum112;intnum2-12;printf(%d\n,num1);printf(%d\n,num1);printf(%d\n,num2);printf(%d\n,num2);return0;}输出结果为可见加上不会影响对负数-的输出4.3 限定小数位数输出小数的时候有时希望限定小数的位数请看下面代码#includestdio.hintmain(){floatnum1.646;printf(%.2f\n,num);printf(%.4f\n,num);return0;}输出结果为%.nf 中n表示限定的小数位数若限定的位数小于实际位数四舍五入若大于实际位数用0补齐除了限定小数的输出位数外还可以限定它的输出宽度只需在 . 前加上限定宽度m如 %m.nf ,输出规则同4.1请看下面代码#includestdio.hintmain(){floatnum1.646;printf(%06.2f\n,num);return0;}输出结果为这里和4.1一样也可以用 * 符号代替m和n把限定数交给后续整型参数动态传入参数匹配同样遵循顺序匹配原则请看下面代码#includestdio.hintmain(){floatnum1.646;intwidth6;intdot2;printf(%0*.*f\n,width,dot,num);return0;}输出结果为4.4 输出部分字符串%s 占位符用来输出字符串默认是全部输出。如果只想输出开头的部分可以用 %.ms 指定输出的长度其中m代表一个数字表示所要输出的长度。需要注意的是这里的限定输出长度不会自动补字符串的结束符\0单纯截断输出请看下面代码#includestdio.hintmain(){printf(%.5s\n,hello world);return0;}输出结果为这里虽然没有在截出的hello后面添加字符串结束符却依然可以正常打印是因为格式符 %.5s 的含义是强制只读取并输出接下来的 5 个字节内容printf 拿到指令之后直接连续读取这 5 个字节打印读完 5 个就立刻停止读取根本不会往后访问自然用不到末尾的终止符 \0。单纯 %s 没有长度限制它会一直向后逐个字节读取、打印直到碰到 \0 才停下。如果没有 \0%s 会越界乱读内存造成乱码、程序崩溃。字符串的输出也遵循4.1的限定宽度规则也支持符号 * 的动态参数传递下面直接用代码说明#includestdio.hintmain(){intlen5;inttotal10;printf(%*.*s\n,total,len,hello world);return0;}输出结果为二scanf1.功能介绍scanf() 函数用于读取用户的键盘输入程序运行到这个语句时会停下来等待用户从键盘输入用户输入数据、按下回车键后scanf() 就会处理用户的输入将其存入变量它的原型定义在头文件 stdio.h2.使用2.1 基本使用scanf() 的语法跟 printf() 类似请看下面代码#includestdio.hintmain(){intnum;scanf(%d,num);}它的第一个参数是一个格式字符串里面放置占位符与 printf() 的占位符基本一致告诉编译器如何解读用户的输入需要提取的数据是什么类型这是因为 C 语言的数据都是有类型的scanf() 必须提前知道用户输入的数据类型才能处理数据注意:这里的占位符之间没有任何字符隔开有分隔符的用法介绍在下面的高级用法里它的其余参数就是用来存放用户输入的变量格式字符串里面有多少个占位符就有多少个变量上面示例中scanf() 的第一个参数 %d表示用户输入的应该是一个整数。第二个参数 num 表示将用户从键盘输入的整数存入变量 num注意变量前面必须加上 运算符指针变量除外因为 scanf() 传递的不是值而是地址即将变量 i 的地址指向用户输入的值如果这里的变量是指针变量比如字符串变量那就不用加 运算符,因为指针变量的值就是地址下面是一次从键盘输入读入多个变量的例子#includestdio.hintmain(){intnum1,num2;floatf1,f2;scanf(%d%d%f%f,num1,num2,f1,f2);}2.2 高级用法a.%[ ]扫描集上面介绍了scanf的基本使用语法下面补充更高级的%[ ]扫描集的使用什么是%[ ]扫描集%[] 是 scanf 里专门自定义可接收字符范围的格式控制符用来精准限定哪些字符允许读入、读到哪个字符停下它的核心逻辑是逐个拿缓冲区字符和 [ ] 内部设定的字符比对匹配集合内的字符正常读取存入变量碰到不在集合里的字符立刻终止读取该字符留在输入缓冲区。它有以下两种写法正向匹配只接收括号里写的字符格式%[字符1字符2字符3……]示例charbuf[100];scanf(%[abc123],buf);上面的例子中scanf只会读取 a、b、c、1、2、3一旦遇到别的字符就会直接停止读取。反向匹配最常用^ 取反遇到指定字符就停止格式%[^字符1字符2字符3……]经典场景读取一整行带空格的字符串读到换行为止示例charbuf[100];scanf(%[^abc123],buf);上面的例子中scanf只会读取除了 a、b、c、1、2、3以外的其他字符一旦遇到这些字符就会直接停止读取。另外[ ] 内支持用-表示连续一段字符搭配^反向同样生效%[^0-9]等价于%[^0123456789]碰到0~9任意数字就停止b.被修饰的%s占位符scanf () 将字符串读入字符数组时不会检测字符串是否超过了数组长度。所以储存字符串时很可能会超过数组的边界导致预想不到的结果。为了防止这种情况使用 % s 占位符时应该指定读入字符串的最长长度即写成 %ns其中的 n 是一个整数表示读取字符串的最大长度后面的字符将被丢弃。请看下面代码#includestdio.hintmain(){chararr[5]{0};scanf(%4s,arr);return0;}上面代码中字符数组arr仅能容纳5个字符scanf的%s占位符被4修饰表示它只会读取4个字符而第五个字符会被自动补为\0在下面处理规则部分有介绍c.格式字符串中的分隔符c.1 分隔符的影响scanf 格式串里有两种“分隔符号”写在格式串里的普通字符- / , 这类肉眼可见符号格式控制自带空白分隔空格、\t、\n当格式字符串中无分隔符时scanf处理普通字符严格按照占位符匹配进行解析而对空白分隔符的处理规则在下面的3.2部分有说道当格式字符串中有分隔符且为普通字符时这个字符就变成了强制匹配字符请看下面例子scanf(%d-%d,a,b);它的处理逻辑是读完第一个整数之后scanf 会立刻从输入缓冲区取下一个字符它会强制判断这个字符是否等于格式字符串里写的普通字符-只有匹配上了它才会继续读取后面第二个整数若拿到的字符不是-匹配会直接卡死后续所有读取会全部作废。当格式字符串中有分隔符且为空格时这个字符表示吃掉输入中任意数量的连续空白字符请看下面例子scanf(%d %c,num,ch);输入12a当占位符间没有空格时scanf执行%d读取整数提取数字12存入num数字读取结束后输入缓冲区当前剩余第一个字符是12和 a 之间的空格%c不会自动跳过任何空白字符直接读取这个空格赋值给ch缓冲区里的字母a会被遗留下来本次读取完全拿不到 a。最终结果num 12ch为空格字符。当占位符间有空格时%d照常读取12存入num格式串里两个占位符之间的空格规则是吞噬输入流里任意一段连续空白字符会把紧跟着的空格直接读取并丢弃空白全部清理完毕后后面的%c才会读取到有效字符a最终结果num 12ch ‘a’。这里补充一个利用空格分隔符处理缓存开头空白字符的技巧如果在格式串最前面手动加一个空格例如 scanf( %c, ch)、scanf( %[^\n], buf)格式串里的空格会自动吞噬前方全部空白字符就能规避读到残留换行的问题。c.2 赋值忽略符*在上面提到的“ 格式字符串中有分隔符且为普通字符 ”的情形中普通字符作为一种强制匹配字符实现了对输入格式的严格控制若格式不匹配就会读取失败比如说输入日期要求格式为 年-月-日scanf(%d-%d-%d,a,b,c);但如果我们对这个输入日期间的分隔符没有定死为“ - ”或者说我们想要实现对使用其他分隔符的日期输入的兼容如“ 2024/7/3 ”我们可以利用赋值忽略符*来将数值之间的分割字符忽略掉*的用法如下scanf(%d%*c%d%*c%d,a,b,c);字符%*c中%表示开启格式解析c表示解析匹配的数据类型为字符*为赋值忽略标记%*c作用是读取当前输入里的单个字符读取之后直接丢弃不存入任何变量、不计入 scanf 返回值用*修饰的占位符会自动忽略掉其匹配到的输入内容所以上面的例子中无论输入的是2024-1-2还是2024/1/2数字间的分隔符都会被匹配忽略数字都能正常地被读取从而实现了输入格式的兼容3.处理规则要想正确使用scanf除了知道使用语法还要知道它处理输入的具体规则。3.1 底层输入缓存工作机制scanf () 处理用户输入的原理是用户键盘输入的内容不会立刻交给scanf处理而是先放入缓存等到按下回车键后按照占位符对缓存进行解读。解读用户输入时会从上一次解读遗留的第一个字符开始直到读完缓存或者遇到第一个不符合条件的字符scanf才会停止读取。如果还没读完缓存就停止了读取剩下的字符会留在缓冲区留给后续scanf继续处理。。3.2 不同占位符对空白字符的差异化处理规则a. 数值类占位符%d、%f、%lf等在读取数字类占位符时scanf 会自动跳过前置所有空白字符空格、Tab 制表符、换行回车、垂直制表符等空白符号全部直接忽略。数据之间不管是 1 个空格、连续多个空格或是中途按回车把数据分成多行输入都不会干扰数值的正常读取解析。b. %c、%[ ]扫描集不会自动跳过前置空白字符会直接读取缓冲区现存的空格、换行符、制表符等空白符号这也是连续读取字符时容易误读到残留回车的核心原因。c. 无修饰的%s占位符仅自动跳过最开头的空白字符之后持续读取非空白内容一旦再次碰到任意空格、回车、制表符这类空白字符就立刻终止读取并且会自动在读取的字符串末尾填充\0结束符。4.scanf 的返回值scanf () 的返回值是一个整数表示成功读取的变量个数。如果没有读取任何项或者匹配失败则返回 0。如果在成功读取任何数据之前发生了读取错误或者遇到读取到文件结尾则返回常量 EOF (-1)。EOF - end of file 文件结束标志
返回列表