C言語 - 第3章 - フォーマットされた出力\入力

免責事項:この記事はブロガーオリジナル記事です、続くBY-SAのCC 4.0を著作権契約、複製、元のソースのリンクと、この文を添付してください。
このリンク: https://blog.csdn.net/cb_east/article/details/97616024


この章では、フォーマットされた出力を記述する\形式の入力を理解するために、関連する知識や困難を入力してください。

3.1出力関数のprintf

関数の値を変換する文字列型を含む文字列を出力するためのprintf関数。Fのprintfは、Word形式の最初の文字です。の形でその使用

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

フォーマット文字列は、関数の第1引数として、コンテンツに対応して出力されます。通常の文字の1、直接出力する場合。同様の%のDは、%F変換指定子、値が記載されて表示される場合、%dは整数型の値に対応する浮動小数点型の対応する値F%は、値がprintfの後に特定の機能に出力しますパラメータ。例えば、整数変数の年齢は、10歳で必要とされる出力値、および出力は、printf関数の呼び出しは次のように「*歳で年齢」の全文です。

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

:あなたは、float型の変数高さ、printf関数呼び出しを表示したい場合は

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

これは、式演算子で表示されることがあります

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

また、printf文で表現の異なる種類を表示することができます

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

printf式の機能ディスプレイの数ができなく表現、我々が見てきたように最初のプログラムとして、唯一の文字列「こんにちは、世界!」、任意であり、

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

ここでは、この問題は、変換は、シンボルと式の間の対応関係を説明することに留意すべきです。実際にはバイナリ値(メモリ内の値をバイナリ形式で格納されている)、値の種類を説明するための、変換指定のprintf関数、バイナリ値の解釈は異なっているので、値型指定子および発現を変換しなければなりません一貫性のあります。

次の種類の変数の場合

int age = 20;
float height = 1.7;

以下のリストは、変換指定のいくつかのタイプの値を示します。

式のタイプ 輸出 変換指定
整数 整数 D printf( "%d個"、年齢); //出力10
フロート 浮動小数点(10進数) F printf( "%のF"、高さ); //出力1.700000
フロート 浮動小数点(指数) E printf( "%のE"、高さ); //出力1.700000e + 000
フロート 浮動小数点(末尾のゼロを示していない小数指数形態と短い形態) グラム printf( "%のG"、高さ); //出力1.7

FまたはG L、Eの直接使用を増加させないようにfloat型のprintf関数型二値の値が、C99の前に、C99は%ル、ダブルタイプの出力値%のLFおよび%LGを用いてprintf関数を許可します。

以下の出力の内容を示すprintf関数のprintf関数のフォーマット文字列の例です。
ここに画像を挿入説明

出力値、異なる種類、異なる文字変換指定の値の型を指定するためのフォーマット文字列の変換指定:

ここに画像を挿入説明

3.1.1洗練変換指定

変換指定は、表示される文字のより正確な制御を行うことができます。例えば、我々は浮動小数点を表示するには、%の.1fを使用することができ、効果が小数点以下のみ小数点以下1桁で示されています。より一般的には、転化率は%-m.pX%m.pX形式またはフォーマットを説明することができます。ここで、mおよびpは整数定数であり、そしてXは文字です。mおよびpは任意です。小数点は、p、mおよびpの間に省略されている場合も除去されます。変換指定%の10.2fは、mが10であり、pは2であり、XはFです。変換指定%10F、mが10であり、(共に小数点)pが省略され、変換命令に%.2f、pは2であり、mが省略されています。

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中。

ここに画像を挿入説明
输入以上字符序列,并按下换行符
ここに画像を挿入説明
不同于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
ここに画像を挿入説明
输入回车键
ここに画像を挿入説明
如果输入的是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函数才能结束运行。

ここに画像を挿入説明
例子中,在输入123后连续输入了多个换行符,没有用,程序没有结束,直到输入了某个非空白字符,这里输入了字符a,再按下换行符,程序输出printf运行结果,再按下换行符,运行结束。

3.2.4 scanf函数的常见问题

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

3.2.5 使用getchar函数暂停程序

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

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

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

ここに画像を挿入説明

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

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

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

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

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

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

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

ここに画像を挿入説明
输入123和换行符,程序可以暂停下来,直到用户输入换行符。

ここに画像を挿入説明
输入小数点、4、换行符后程序可以暂停下来,直到用户输入换行符。

以下内容不做要求:

我々はGETCHAR機能の異なる数を使用していますが、プログラムが一時停止することができますが、GETCHAR必要性の数は、あなたの入力は、時間を実行し、そして自分自身をのgetcharに関連するされたコードをコンパイルするために書かれたプログラムですが、コードを書くときプログラムが予測できない動作しているときに、いくつかの文字の入力バッファに残って。したがって、GETCHARの必要な数は予測不可能です。同時に、我々はscanfの声明は、プログラマ、プログラマーの場合は、見たくないです、その後のscanf関数を、読んで、その後になる前に、実行時の文字列を残し、プログラムの入力バッファを共有している、と述べています各scanf関数は、ユーザーが再起動することを可能にすることが一般的に望ましいです。次scanf関数を実行する前にそのため、入力バッファが非常に必要で空にされます。次のコード入力バッファを空にすることができると

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

それは、ファイルのEOFまたは改行の終わりに遭遇するまで、上記の文の機能は常に、getchar関数で文字入力バッファを読んでいます。(入力バッファの最後の文字は、ファイルのEOFの終わりではない、改行文字「\ n」があります。)

番組の一時停止を行うために、長いGETCHAR関数前に入力バッファをクリアするために上記のコードを追加します。このように、入力バッファに残っているどんなに多くの文字、それをクリアすることができます。

ここに画像を挿入説明
あなたは再び改行を入力するまで123や改行を入力し、プログラムが終了しません。

ここに画像を挿入説明
小数点、4、改行再度改行を入力するまで、プログラムが終了しません。

おすすめ

転載: blog.csdn.net/cb_east/article/details/97616024