C language - Chapter 3 - formatted output \ input

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/cb_east/article/details/97616024


This chapter describes the formatted output \ enter the relevant knowledge and difficult to understand format input.

3.1 output function printf

printf function for outputting a string containing a string type to convert the value of the function. The f printf is the first letter of the word format. Its use in the form of

printf(格式串,表达式1,表达式2, ...);

Format string, as the first argument of the function, corresponding to the content to be outputted. If one of ordinary character, the direct output. If similar% d,% f conversion specifier, the value to be displayed is described,% d corresponding to the value of integer type,% f the corresponding value of the floating point type, the value to be output on a specific function after printf parameter. For example, the integer variable age is 10, the age required output value, and the output is the full text of the "age is * years old", printf function call as follows:

printf("年龄是%d岁\n", age);

If you want to display a float type variable height, the printf function call:

printf(“身高是%f\n”, height);

It may be displayed with an expression operators

printf("年龄是%d岁,下一年的年龄是%d\n", age, age + 1);

It can also display different types of expressions in a printf statement

printf("年龄是%d岁,身高是%f\n", age, height);

printfThe number of expressions functions display is arbitrary, can no expression, such as the first program we've seen, only the string "hello, world!"

printf("hello, world!\n");

Here, the problem should be noted that the conversion explain the correspondence between symbols and expressions. In fact, the conversion specifier printf function to help explain the binary values ​​(values ​​in memory are stored in binary), different types of values, a binary value interpretation is different, and therefore must be converted value type specifier and expression consistent.

If the following types of variables

int age = 20;
float height = 1.7;

The following list shows the values ​​of some types of conversion specifier.

Expression Type Export Conversion specifier example
Integer Integer d printf ( "% d", age); // Output 10
Float Floating-point (decimal) f printf ( "% f", height); // output 1.700000
Float Floating point (exponential) e printf ( "% e", height); // output 1.700000e + 000
Float Floating-point (decimal exponential form and short form that does not show trailing zeros) g printf ( "% g", height); // output 1.7

Prior to the C99, with the value of printf type double value of type float as not to increase L, the direct use of e, f or g, C99 allow the printf function using% le,% lf and% lg to the output value of type double .

Below is an example of a printf function printf function format string indicating the contents of the output:
Here Insert Picture Description

Format string conversion specifier for specifying the type of output values, values ​​of different types, different character conversion specifier:

Here Insert Picture Description

3.1.1 refinement conversion specification

Conversion specification can do more precise control of the characters displayed. For example, we can use% .1f to display floating point, the effect is shown with only one decimal place after the decimal point. More generally, the conversion can be explained% -m.pX% m.pX format or formats. Where m and p are integer constants, and X is the letter. m and p are optional. If the decimal point is omitted between p, m and p are also removed. Conversion specifier% 10.2f, m is 10, p is 2 and X is f. Conversion specifier% 10f, m is 10, p (together with the decimal point) is omitted; in the conversion instructions% .2f, p is 2, m is omitted.

m用来指定要显示的表达式占的最小字段宽度。 如果待显示的字符数量小于m,那么整个表达式占m个字段宽度,实际显示的字符右对齐显示。比如,转换说明%4d将以" 123"显示整数123。如果在m之前放负号,则左对齐。比如,转换说明%-4d将以"123 "显示整数123。如果待显示的字符数量大于等于m,那么忽略m值,以待显示的字符数量决定整个表达式占的字段宽度。比如,转换说明%4d将以12345的形式显示12345,不会丢失数字。

p的含义很难描述,因为它依赖转换说明符X的选择。

  • f - 表示显示“定点十进制”形式的浮点数。p指明了待显示的浮点数的小数点后显示的数字的个数(默认是6,不足补零)。如果p为0,则不显示小数点和其后的小数。

  • e - 表示显示指数形式的浮点数。p的含义与在f说明符中的一样。

  • g - 表示以“定点十进制”或者指数形式来显示浮点数。选择它们中最短的那个。p的含义是显示的有效数字的最大数量。比如,%.3g将以"1.23"显示1.2345。与f不同,它不显示尾随的零。比如,%.5f将以"1.23000"来显示1.23,而%.5g将以"1.23"来显示1.23。

  • d - 表示显示整数值。p指明了待显示的数字的最少个数(必要时在前面加上额外的零);如果省略p,则默认它的值是1。如果待显示的数字个数大于p,则按照实际数字个数来显示。比如,转换说明%.4d将以"0020"显示整数20,转换说明%.2d将以“2000”显示整数2000。(这个用法用的较少)

下面的程序举例说明了用printf函数以各种格式显示整数和浮点数的方法。

/*用各种格式输出整数和浮点数*/

#include <stdio.h>

int main(void)
{
  int i;
  float x;

  i = 40;
  x = 839.21f;

  printf("|%d|%5d|%-5d|%5.3d|\n", i, i, i, i);
  printf("|%10.3f|%10.3e|%-10g|\n", x, x, x);
  
  return 0;
}

在显示时,printf函数格式串中的字符 |只是y用来帮助显示每个数所占用的空格数量;不同于%或\,字符|对printf函数而言没有任何特殊意义。此程序的输出如下:

formatprintf
下面仔细看一下上述程序中使用的转换说明。

  • %d — 以十进制形式显示变量i,且占用最少的空间。
  • %5d — 以十进制形式显示变量i,且至少占用5个字符的空间。因为变量i只占两个字符,所以添加了3个空格。
  • %-5d — 以十进制形式显示变量i,且至少占用5个字符的空间。因为表示变量i的值不需要用满5个字符,所以在后续位置上添加空格(更确切地说,变量i在长度为5的字段内是左对齐的)。
  • %5.3d — 以十进制形式显示变量i,且至少占用5个字符的空间并至少有3位数字。因为变量i只有2个字符长度,所以要添加一个额外的0来保证有3位数字。现在只有3个字符长度,为了保证占有5个字符,还要添加2个空格(变量i是右对齐的)。
  • %10.3f — 以定点十进制形式显示变量x,且总共用10个字符,其中小数点后保留3位数字。因为变量x只需要7个字符(即小数点前3位,小数点后3位,再加上小数点本身1位),所以在变量x前面有3个空格。
  • %10.3e — 以指数形式显示变量x,且总共用10个字符,其中小数点后保留3位数字。因为变量x总共需要10个字符(包括指数),正好占用了它应该占用的10个字符。
  • %-10g — 既可以以定点十进制形式显示变量x,也可以以指数形式显示变量x,且总共用10个字符。在这种情况下,printf选择短一点的定点十进制形式显示变量x。负号的出现强制进行左对齐,所以有4个空格跟在变量x的后面。

3.1.2 转义序列

格式串中常用的\n表示转义序列。转义序列是字符串包含一些特殊的字符而不会使编译器引发问题,这些字符包括非打印的字符(控制字符)以及一些对编译器有特殊含义的字符(如")。

  • 警报符:\a
  • 回退符:\b
  • 换行符:\n
  • 水平制表符:\t

字符\a会产生一个鸣响,\b会使光标从当前位置回退一个位置。\n输出换行符,使光标跳到下一行的起始位置。\t使输出从下一个制表位开始。

其它一些常见的转义序列有:

  • 双引号 " : 由于双引号用来表示字符串的结束,故字符串要含有",需要用\对它进行转义。
  • 反斜杠\ : 反斜杠用来表示对字符进行转义,因此要表示具体字符反斜杠\,需要用\对反斜杠进行转义。

3.1.3 printf常见问题

参考第2章-printf常见问题

3.2 输入函数scanf

printf函数将字符或者数值输出成字符串显示到计算机外部,而scanf函数恰好相反,从计算机外部将字符串读入到计算机内部。scanf中的f也是format的首字母。其一般格式如下:

scanf(格式串, 变量地址, 变量地址, ...);

在许多情况下,scanf函数的格式串中只需要包含转换说明,比如

int i, j;
float x, y;

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

假设用户输入了下列内容并按回车:

1 -20 .3 -4.0e3

scanf函数将读入上述字符序列,并转换为“%d%d%f%f”标识的int整数、int整数、float浮点数、float浮点数,并依次保存到变量i,j,x,y中。

Here Insert Picture Description
输入以上字符序列,并按下换行符
Here Insert Picture Description
不同于printf函数,scanf基本不用出现除转换说明外的其它字符。scanf函数中像"%d%d%f%f"这样的“紧密压缩”的格式说明符很普遍,而printf函数的格式串中很少有这样紧挨着的转换说明,否则输出的数值之间就没有间隙,用户无法分辨出各个数值。

类似于printf函数,使用scanf函数时也要注意转换说明和变量的个数的匹配,类型的匹配。变量之前要加&符号来取得变量的在内存中的地址(尽管不是所有的情况都要加&)。

变量类型 输入 转换说明符 例子
int 十进制整数 d scanf("%d", &age); // 输入10
float “定点十进制”或者指数形式的浮点数 f或者e或者g scanf("%f", height); // 输入 1.7或者0.17e1
double “定点十进制”或者指数形式的浮点数 lf或者le或者lg scanf("%lf", height); // 输入 1.7或者0.17e1

注意:

  • 输入浮点数时,不论采用的是“定点十进制”还是指数形式,转换说明f、e或者g是通用的。
  • 向double类型变量输入时,在f、e或者g前面加字母l。

3.2.1 scanf函数的工作方法

scan单词的意思是扫描,scanf函数本质上是一种“模式匹配”函数,它会把输入的内容看成字符序列,然后把这个字符序列与格式串相匹配,直至格式串中的全部内容得到处理。值得注意的是,scanf函数的扫描过程是在按下回车键才真正执行的,而不是输入一个字符就处理一下,在这之前用户输入的内容是保存在输入缓冲区里面的,并不会丢失。

按下回车键scanf函数真正开始执行后,它会以格式串中的转换说明来匹配/解读输入缓冲区中的字符序列,匹配成功会将其表示的值存储到相应的变量中,然后继续以格式串中剩余的转换说明来匹配输入缓冲区剩余的字符序列。直到格式串中的转换说明全部被匹配,scanf函数执行结束。输入缓冲区中剩余未被处理的字符序列会被后续的输入函数来处理。

在匹配数值类型输入的时候,scanf函数会忽略输入的数值序列之前的空白字符。包括空格符、水平制表符、换行符在内的字符称为空白字符。读取到数值之后的空白字符意味着当前的数值的字符序列结束了。数值字符序列被解读为数值并保存到变量中,而输入缓冲区中数值字符序列之后的空白字符会留下来待后续的输入函数来读取。

比如,考虑下面的scanf函数调用:

scanf("%d%d", &i, &j);

如果输入了:12 345回车
即字符1、2、空格、3、4、5、换行符
那么scanf函数会以第一个%d意味着匹配整数并放到变量i中。它先后读取字符1、2、空格,读取到空格后知道整数字符序列结束了,字符1、2会被转换为数值12并保存到变量i中。而空格会被放回输入缓冲区。接下来,scanf函数会匹配第二个%d。此次依然是匹配输入的整数。在读取并跳过空格后,字符3、4、5会被读取,直到读取到换行符,表明整数序列的结束,字符3、4、5会被转换为数值345并保存到变量j中。而换行符会被放回输入缓冲区,会被后续输入函数读取到。

需要注意的是,如果在匹配转换说明和输入字符序列的过程中失败,scanf会停止后续的匹配过程。比如

scanf("%d%d", &i, &j);

如果输入了:1 .5回车
即字符1、空格、小数点、字符5、换行符
第1个%d意味着匹配整数放到变量i中,1和空格被依次读取并扫描,由于空格不属于整数,故字符1被转换为整数1并赋值到i中,空格被放回输入缓冲区。接下来,第2个%d意味着继续匹配整数并放到变量j中,那么在匹配过程中,首先忽略空格,然后扫描到小数点,由于整数不会包含小数点,故这意味着匹配的结束,而此时并没有读到任何整数字符,这意味着输入内容没有遵循转换说明符的要求输入一个整数,是表示输入出错了,故在小数点被放回输入缓冲区后,scanf函数运行结束。i被赋值为1,而j变量未被改变。此时,输入缓冲区还有小数点、字符5和换行符共3个字符。

如果输入的是:1 1.5回车
即字符1、空格、字符1、小数点、字符5、换行符
那么如同上述步骤那样,scanf会把第1个1读取到i中,把第2个1读取到j中,而输入缓冲区中剩余包含小数点和其后的字符串(字符5和换行符)。

在匹配数值的过程中,scanf函数会忽略数值之前的任意多个空白字符。

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

用户可以输入:
1 -20 .3 -4.0e3
注意,各个数之间有一个空格。
或者用户可以输入:
1
-20
.3
-4.0e3
注意,此时各个数之间有一个换行符。
或者,用户可以输入:
  1
-20 .3
    -4.0e3
即各个数之间既有换行符又有空格。

3.2.2 格式串中的普通字符

上面介绍了格式串中最常见的情况,即只包含转换说明。如果格式串中还包括普通字符,那么scanf要求在匹配到格式串中这个普通字符时,输入的字符序列中也是这个字符。如果匹配,scanf会放弃这个字符,继续处理格式串中的后续内容。如果不匹配,那么scanf会把不匹配的字符放回输入缓冲区,然后scanf函数不再向后处理。

比如

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

如果输入的是:5,10回车
即字符5,逗号,字符1、字符0、回车符
那么可以顺利的读入。读入过程是先匹配格式串"%d,%d"和输入字符串"5,10"。格式串中的转换说明符%d被先匹配,5和逗号会依次扫描到,扫描到逗号停止扫描,因为逗号不是数值,5和%d是匹配的,整数5被读入到x中,逗号被放回待扫描的输入缓冲区中。接下来,scanf会匹配格式串中普通字符逗号。恰好输入缓冲区中是逗号,它们相匹配。scanf会放弃它,继续匹配格式串"%d",于是10被匹配。scanf完成整个格式串的处理。

输入5,逗号,10
Here Insert Picture Description
输入回车键
Here Insert Picture Description
如果输入的是5 10(5和10之间只有一个空格,没有逗号)

那么读入过程中会出错。格式串中的转换说明符%d被先匹配,5和空格会依次扫描到,扫描到空格停止扫描,因为空格不是数值,5和%d是匹配的,数值5被读入到x中,空格被放回待扫描的输入缓冲区中。接下来,scanf会匹配格式串中普通字符逗号。格式串中的普通字符逗号和输入缓冲区的空格不匹配,故scanf函数出错,scanf停止执行。

上述过程,进一步说明了printf的函数中的格式串指出了输出内容,而scanf函数的格式串指出了输入的内容。

3.2.3 格式串中的空白字符

一种特殊的情况,如果格式串中出现的普通字符是空白字符。格式串中的一个或者多个连续空白字符与输入字符串中的任意个(包括零个)空白字符匹配。但是,这个匹配过程会一直持续下去,直到遇到输入字符串中的某个非空白字符才会停止,该非空白字符会放回输入缓冲区待用。

这解释了如果读入一个整数的scanf函数写成了scanf("%d ");(%d后面有一个空格)或者scanf("%d\n");,那么即使用户输入了一个整数并按下了回车,scanf函数还在运行,并没有结束,造成了程序的“挂起”现象。直到用户输入了任意一个非空白字符并再次按下回车键scanf函数才能结束运行。

Here Insert Picture Description
例子中,在输入123后连续输入了多个换行符,没有用,程序没有结束,直到输入了某个非空白字符,这里输入了字符a,再按下换行符,程序输出printf运行结果,再按下换行符,运行结束。

3.2.4 scanf函数的常见问题

参考第2章-scanf函数的常见问题

3.2.5 使用getchar函数暂停程序

VC开发环境下,在debug模式下运行程序,程序运行结束会自动销毁窗口,如果程序中没有输入函数的调用,程序会一闪而过。
为了解决这个问题, 我们在程序的最后(return 0;语句之前)放置一个scanf函数来尝试读取一个字符,这样程序运行到那里就会暂停下来等待用户输入字符,故之前的运行结果即可被显示出来。

Here Insert Picture Description
如上调用scanf函数会将输入的字符保存到字符变量ch中。其实输入数值本质上也是将输入的内容看成字符序列,只是scanf函数会将它们转换为相应的整数。除了scanf可以输入字符,getchar函数也具有读取输入的一个字符的功能,我们可以用它来替代scanf函数。
ch = getchar();

getchar函数会返回读取到的字符值,ch = getchar();会将这个字符值赋值给字符变量ch,所以效果上和用scanf是一致的。由于这里读取字符的目的只是暂停程序,并不对输入的字符值本身感兴趣,所以可以像如下那样调用

Here Insert Picture Description

这样getchar函数依然会读取一个字符,只是没有把它保存到字符变量中,所以在这条语句之后这个字符的值就无法访问了。

那么为什么在某些情况下,只调用一次getchar函数还是无法让程序暂停下来呢?

首先,从原理上讲,一个程序中有唯一一个输入缓冲区,用户的输入都会暂存到那里,供程序中的scanf、getchar等输入函数来读取。而只有在输入缓冲区为空的情况下getchar才会让程序停下来。因为此时,getchar函数会阻塞住,等用户输入字符后存入输入缓冲区后,它才能读到字符并结束运行。否则如果输入缓冲区中原先就有字符,getchar将会直接从中读取字符并结束运行,所以用户没有机会输入,窗口也会一闪而过。

什么时候输入缓冲区会有遗留的尚未被读取的字符呢,很可能是之前运行的输入函数没有将输入缓冲区中用户输入的字符读取完,还剩有一些。比如,

  1. scanf函数正常运行,输入缓冲区中留下的字符是换行符。
    Here Insert Picture Description
    上述程序在输入123后再输入换行符,程序会一闪而过。这是由于scanf函数运行时需要按下换行符触发其真正执行。故scanf函数输入了字符串123和换行符后,scanf函数只读取了其中的字符串123(并将其转换为整数123并保存到变量x中),可是换行符仍旧在输入缓冲区中。故getchar函数运行时输入缓冲区并不空,在getchar读取了换行符后getchar函数结束运行,程序也即将结束,会有一闪而过的现象。

  2. scanf函数运行中用户遇到了不匹配的内容,输入缓冲区中留下了scanf函数尚未读取的若干不匹配字符。
    Here Insert Picture Description
    上述程序运行过程中,用户输入了3个字符,小数点和4以及换行符。由于scanf函数中的转换说明符是%d,故scanf尝试从输入字符串中读取整数,但是第一个字符小数点就不是整数的一部分,故scanf函数并没有读取任何整数就结束了,而用户输入的3个字符仍然在输入缓冲区中。故getchar函数运行时直接从输入缓冲区中读取了第1个字符,小数点。随即getchar函数运行结束,程序运行结束,窗口会一闪而过。

在如上述两种情况下,如何让程序暂停下来?办法很简单,getchar函数的调用次数至少超过输入缓冲区尚未被读取的字符数量即可。第1个例子中,getchar运行时输入缓冲区有1个字符,换行符,故共放置2条getchar函数调用即可。第2个例子中,getchar运行时输入缓冲区中有3个字符,小数点、字符4、换行符。故共放置4条getchar函数即可。

Here Insert Picture Description
输入123和换行符,程序可以暂停下来,直到用户输入换行符。

Here Insert Picture Description
输入小数点、4、换行符后程序可以暂停下来,直到用户输入换行符。

以下内容不做要求:

Although we use a different number of getchar function allows the program to pause, but the number of getchar need is your input is related to run-time, and getchar itself is a program written to compile the code, but when writing code left in the input buffer of several characters when the program is running unpredictable. Therefore, the required number of getchar is unpredictable. At the same time, we have said, a program input buffer is shared, left behind a string of run-time before scanf statement is then read subsequent scanf function, which is the case of programmers do not want to see, the programmer It is generally desirable each scanf allows users to start again. Therefore, the input buffer is emptied very necessary before the next scanf function runs. With the following code input buffer may be emptied

while((ch = getchar()) != EOF && ch != '\n');

Functions of the above statement is constantly reading character input buffer with getchar, until it encounters an end of file EOF or newline. (Input buffer last character is not end of file EOF, is the newline character '\ n'.)

In order to make the program pause, add the above code to clear the input buffer long before a getchar function. In this way, no matter how many characters are left in the input buffer, it can be cleared.

Here Insert Picture Description
Enter 123 and line breaks, the program will not end until you enter a line break again.

Here Insert Picture Description
Decimal point, 4, line breaks, the program will not end until you enter a line break again.

Guess you like

Origin blog.csdn.net/cb_east/article/details/97616024