[页眉: 51. C++20 格式化字符串]
1. C 语言格式化输入输出
1) 类型占位符:
①格式化为以下类型时的类型占位符:[qb:
+------------------------------------------+--------+
| integer | type |
+------------------------------------------+--------+
| (integer) char | |
| short | |
| int | |
| long | |
| long long | |
| | |
| unsigned char | |
| unsigned short | |
| unsigned int | |
| unsigned long | |
| unsigned long long | |
| | |
| (octal) unsigned char | |
| (octal) unsigned short | |
| (octal) unsigned int | |
| (octal) unsigned long | |
| (octal) unsigned long long | |
| | |
| (hex lowercase) unsigned char | |
| (hex lowercase) unsigned short | |
| (hex lowercase) unsigned int | |
| (hex lowercase) unsigned long | |
| (hex lowercase) unsigned long long | |
| | |
| (hex uppercase) unsigned char | |
| (hex uppercase) unsigned short | |
| (hex uppercase) unsigned int | |
| (hex uppercase) unsigned long | |
| (hex uppercase) unsigned long long | |
+------------------------------------------+--------+
| float | type |
+------------------------------------------+--------+
| (not input) float | |
| (not input) double | |
| (input) float | |
| (input) double | |
| (scientific notation e-lowercase) float | |
| (scientific notation e-uppercase) double | |
| (scientific notation e-lowercase) float | |
| (scientific notation e-uppercase) double | |
| (general format) float | |
| (general format) double | |
+------------------------------------------+--------+
| other | type |
+------------------------------------------+--------+
| address | |
| char | |
| string | |
+------------------------------------------+--------+
][b:
+------------------------------------------+--------+
| integer | type |
+------------------------------------------+--------+
| char | %hhd |
| short | %hd |
| int | %d |
| long | %ld |
| long long | %lld |
| | |
| unsigned char | %hhu |
| unsigned short | %hu |
| unsigned int | %u |
| unsigned long | %lu |
| unsigned long long | %llu |
| | |
| (octal) unsigned char | %hho |
| (octal) unsigned short | %ho |
| (octal) unsigned int | %o |
| (octal) unsigned long | %lo |
| (octal) unsigned long long | %llo |
| | |
| (hex lowercase) unsigned char | %hhx |
| (hex lowercase) unsigned short | %hx |
| (hex lowercase) unsigned int | %x |
| (hex lowercase) unsigned long | %lx |
| (hex lowercase) unsigned long long | %llx |
| | |
| (hex uppercase) unsigned char | %hhX |
| (hex uppercase) unsigned short | %hX |
| (hex uppercase) unsigned int | %X |
| (hex uppercase) unsigned long | %lX |
| (hex uppercase) unsigned long long | %llX |
+------------------------------------------+--------+
| float | type |
+------------------------------------------+--------+
| (not input) float | %f/%lf |
| (not input) double | %f/%lf |
| (input) float | %f |
| (input) double | %lf |
| (scientific notation e-lowercase) float | %e |
| (scientific notation e-lowercase) double | %le |
| (scientific notation e-uppercase) float | %E |
| (scientific notation e-uppercase) double | %lE |
| (general format) float | %g |
| (generao format) double | %lG |
+------------------------------------------+--------+
| other | type |
+------------------------------------------+--------+
| address | %p |
| char | %c |
| string | %s |
+------------------------------------------+--------+
]
2) 常用的数值形式:
①有符号十进制整数:[i:有符号十进制整数的格式如下][b:
[-]dd···d
// 正数时无符号位(注意并不是“有符号位只是不显示正号”),负数时有符号位且为负号
// 进制为十进制,即每个数码 d 的取值范围为 [0, 9] 之间的整数
// 位数没有特殊限制(但实际开发中需注意避免数据溢出,如用 char 类型的变量保存大于 127 的整数)
]
2) 与整数相关的格式化占位符:
①整数相关的占位符及其含义:[i:与整数相关的格式化占位符有 %d、%u、%o、%x、%X,分别表示匹配一个有符号十进制整数、无符号十进制数、无符号八进制整数、无符号十六进制整数(字母数码小写)、无符号十六进制整数(字母数码大写)整数进行输入或输出;「i:例如」][b:「b:
::printf("%d\n", 10); // 10,即 %d 会匹配 10 并以十进制整数的格式输出
::printf("%d %d\n", -3, false); // -3 0,即第一个 %d 会匹配 -3、第二个 %d 匹配 false,它们都会以十进制整数的格式输出
::printf("%o\n", 0xa); // 12,即 %d 会匹配 0xa 并以八进制的格式输出
」]
②匹配非整数数据的输出结果:[i:匹配以下非整数类型的数据时,输出结果为
>a. 匹配浮点数(float/double):输出结果是不确定的,因为这是未定义行为(注意不是做隐式类型转换、只输出整数部分,事实上,编译器往往直接把浮点数对应的内存块中的数据按照整数的存储格式进行解释并以十进制的形式输出);
>b. 匹配布尔值:匹配 true 时输出 1,匹配 false 时输出 0(广义上布尔型也属于整型,例如 C 语言中没有布尔型,直接用 1 表示真,用 0 表示假);
>c. 匹配字符值:字符型本质就是整型,所以匹配字符值时输出对应的整数;
]
③%d 输出时的默认行为:[i:十进制整数占位符 %d 在输出时会匹配一个数并输出为有符号十进制 int 型整数;「i:例如」][b:「b:
::printf("%d\n", 42); // 42
::printf("%d\n", -42); // -42
::printf("%d\n", 012); // 10
::printf("%d\n", 0xa); // 10
::printf("%d\n", 3.14f); // 无意义(匹配浮点数,未定义行为)
::printf("%d\n", -3.56); // 无意义(匹配浮点数,未定义行为)
::printf("%d\n", 12E+02); // 无意义(匹配浮点数——指数表示法一定是浮点数,未定义行为)
::printf("%d\n", 'a'); // 97('a' 的本质就是 char 类型的整数 97)
::printf("%d\n", true); // 1
::printf("%d\n", INT_MAX + 1); // 无意义(数据溢出、未定义行为)
」]
④%d 输入时默认行为:[i:十进制整数占位符 %d 在输入时会匹配一个有符号十进制整数并输入,具体过程为,若 %d 在缓冲区中对应位置的:
>a. 第一个非空白字符是正号或负号且下一个字符是 [0,9] 之间的整数字符;
>b. 第一个非空白字符是 [0,9] 之间的整数字符;
则提取以上字符并继续提取后续字符,遇到非 [0,9] 之间的字符停止提取;「i:例如」]
[b:「b:
#include
int main(int argc, char** argv) {
int n = 666;
char buf[32] = {0};
::scanf("%d%s", &n, buf);
::printf("n: %d, buf: %s\n", n, buf);
/*
输出结果:
输入 123abc - 输出 n: 123, buf: abc(%d 从字符 “1” 开始提取、它是 [0,9] 之间的整数,继续向后虽然 123abc 可以看成一个十六进制整数,但是 %d 只能匹配 [0,9] 之间的数码)
输入 0x123abc - 输出 n: 0, buf: x123abc(原因同上)
输入 1.23abc - 输出 n: 1, buf: .23abc(虽然 1.23 是一个小数,但是 %d 只能匹配正负号和 [0,9] 之间的数码无法匹配小数点)
输入 -123+123 - 输出 n: -123, buf: +123(第一个字符是负号、第二个字符是 1,匹配成功,然后继续提取 2 和 3,遇到“+”时,由于它不是 [0,9] 之间的数码,所以匹配失败)
输入 +000123true - 输出 n: 123, buf: true(原因同上)
输入 trueabc - 输出 n: 666, buf:(n 为初始值 666 是因为 %d 匹配失败,scanf 直接返回,从而 %s 也未匹配任何内容)
输入 +abc123 - 输出 n: 666, buf:(n 为初始值 666 是因为 %d 匹配失败,即虽然第一个字符是正号,但第二个字符不是 [0,9] 之间的数,导致 scanf 直接返回,从而 %s 也未匹配任何内容)
输入 12ab (含两个前导空格) - 输出 n: 12, buf: ab(%d 会忽略前导空白字符)
*/
int x = 111, int y = 222;
::scanf("%d%d", &x, &y);
::printf("x: %d, y: %d\n", x, y);
/*
输出结果:
输入 123123 - 输出 x: 123123, y: 222
输入 -123+123 - 输出 x: -123, y: 123(第一个 %d 遇到“+”时由于它不是 [0,9] 之间的数码而停止匹配,第二个 %d 匹配时发现最开始的“+”和“1”满足要求,于是继续向后提取,从而成功输入 +123)
输入 123 123 - 输出 x: 123, y: 123(第一个 %d 遇到空格时由于它不是 [0,9] 之间的数码而停止匹配,第二个 %d 匹配时会忽略前导空白字符,即直接从空格后的字符“1”开始匹配,易知也能匹配 123)
*/
::scanf("%d %d", &x, &y);
::printf("x: %d, y: %d\n", x, y);
/*
输出结果:
输入 123123 - 输出 x: 123123, y: 222
输入 -123+123 - 输出 x: -123, y: 222(第一个 %d 遇到“+”时由于它不是 [0,9] 之间的数码而停止匹配,两个 %d 之间的空格要求在输入缓冲区的对应位置也必须出现一个空格,然而实际情况是“+”,于是匹配失败、scanf() 直接返回,第二个 %d 未匹配任何内容)
输入 123 123 - 输出 x: 123, y: 123(第一个 %d 遇到空格时由于它不是 [0,9] 之间的数码而停止匹配,两个 %d 之间的空格要求在输入缓冲区的对应位置也必须出现一个空格,实际情况也确实是空格,所以匹配成功,第二个 %d 匹配时发现最开始的“1”满足要求,于是继续向后提取,从而成功输入 123)
*/
return 0;
}
」]
⑥无符号整数占位符输出时默认行为:[i:无符号整数占位符 %u/%U 在输出时会匹配一个数并输出为以下格式][b:
dd···d
// 无符号位
// 进制为十进制,即每个数码 d 的取值范围为 [0,9]
// 位数没有特殊限制(即位数大于 0 且不会超过 32 位无符号整数的最大值对应的位数)
「i:例如」「b:
::printf("%u\n", 12); // 12
::printf("%u\n", 0xc); // 12
::printf("%u\n", -12); // 无意义(未定义行为),因为 %u 输出结果无符号位,不能输出负数
::printf("%u\n", 014); // 12
::printf("%u\n", 3.14f); // 无意义(未定义行为)
::printf("%u\n", 3.56); // 无意义(未定义行为)
::printf("%u\n", 12E+02); // 无意义(未定义行为),因为指数表示法一定是浮点数
::printf("%u\n", 'a'); // 97
::printf("%u\n", true); // 1
」
]
④八进制整数占位符输出时默认行为:[i:八进制整数占位符 %o 在输出时会匹配一个数并输出为以下格式][b:
dd···d
// 无符号位
// 进制为八进制,即每个数码 d 的取值范围为 [0, 7] 之间的整数
// 位数没有特殊限制(即位数大于 0 且不会超过 32 位八进制整数的最大值对应的位数)
「i:例如」「b:
::printf("%o\n", 10); // 12
::printf("%o\n", -12); // 无意义(未定义行为),因为 %o 输出结果无符号位,不能输出负数
::printf("%o\n", 012); // 12
::printf("%o\n", 0xc); // 12
::printf("%o\n", 3.14f); // 无意义(未定义行为)
::printf("%o\n", 3.56); // 无意义(未定义行为)
::printf("%o\n", 12E+02); // 无意义(未定义行为),因为指数表示法一定是浮点数
::printf("%o\n", 'a'); // 141
::printf("%o\n", true); // 1
」
]
⑤十六进制整数占位符输出时默认行为:[i:十六进制整数占位符 %x/%X 在输出时会匹配一个数并输出为以下格式][b:
dd···d
// 无符号位
// 进制为十六进制,即每个数码 d 的取值范围为 0,1,2,3,4,5,6,7,8,9,a/A,b/B,c/C,d/D,e/E,f/F
// 若使用 %x,则使用小写的字母数码,若使用 %X,则使用大写的字母数码
// 位数没有特殊限制(即位数大于 0 且不会超过 32 位十六进制整数的最大值对应的位数)
「i:例如」「b:
::printf("%x\n", 10); // a
::printf("%x\n", -12); // 无意义(未定义行为),因为 %x 输出结果无符号位,不能输出负数
::printf("%X\n", 012); // A
::printf("%X\n", 0xc); // C
::printf("%x\n", 3.14f); // 无意义(未定义行为)
::printf("%x\n", 3.56); // 无意义(未定义行为)
::printf("%x\n", 12E+02); // 无意义(未定义行为),因为指数表示法一定是浮点数
::printf("%x\n", 'a'); // 61
::printf("%x\n", true); // 1
」
]
⑦整数占位符输出格式控制:[i:整数占位符 %d、%o、%x/%X、%u 的输出格式控制语法格式如下][b:
%[flags][width].[precision][length]d/o/x/X/u
/*
* flags:
* +/空格:源数据为正数时显示正号/空格;可缺省,默认正数时没有符号位,仅负数时显示负号;
* -:%f 默认输出格式的实际字符宽度小于 width 字段指定的字符宽度时左对齐;可缺省,默认右对齐;
* *:对输出格式控制无效;
* 0:使用前导 0,可缺省,默认不使用;具体来说,%f 默认输出格式的实际字符宽度小于 width 字段指定的字符宽度且对其方式为右对齐时用 0 填充空位而非空格;
* #:输出八进制和十六进制整数时显示前缀“0、0x(当占位符为 %x 时)、0X(当占位符为 %X 时)”;
* width:
* 最小字符宽度;可缺省,默认为 0;
* 未限制宽度时的字符宽度小于 width 字段指定的字符宽度时左对齐或右对齐(具体是左对齐还是右对齐取决于 flags 字段)并使用指定字符填充空位(具体使用什么字符填充取决于 flags 字段);
* 未限制宽度的字符宽度大于 width 字段指定的字符宽度时该字段不起作用;
* precision:
* 输出整数时无效;
* length:
* hh:用于输出 char 类型的整数;
* h:用于输出 short 类型的整数;
* l:用于输出 long 类型的整数;
* ll:用于输出 long long 类型的整数;
* 注意:char, short, int, long, long long 的实际宽度取决于当前平台/编译器,对 64 位平台一般分别为 1 字节、2 字节、4 字节、8 字节、8 字节;
*/
「i:例如」「b:
#include
int main(int argc, char** argv) {
// 加号、空格、非十进制前缀
::printf("%d\n", 123456);
::printf("%+d\n", 123456);
::printf("% d\n", 123456);
::printf("%o\n", 123456);
::printf("%#o\n", 123456);
::printf("%x\n", 123456);
::printf("%#x\n", 123456);
::printf("%X\n", 123456);
::printf("%#X\n", 123456);
/*
输出结果:
123456 - 十进制的 123456
+123456 - flags 字段指定了“+”,所以有符号位、显示正号
123456 - flags 字段指定了空格,所以有符号位、显示空格
361100 - 八进制的 123456
0361100 - flags 字段指定了“#”,所以显示八进制前缀“0”
1e240 - 十六进制字母数码小写的 123456
0x1e240 - flags 字段指定了“#”,所以显示十六进制前缀“0x”
1E240 - 十六进制字母数码大写的 123456
0X1E240 - flags 字段指定了“#”,所以显示十六进制前缀“0X”
*/
// 2. (最小字符)宽度、减号、0
::printf("|%10d|\n", -123456);
::printf("|%10u|\n", 123456);
::printf("|%7x|\n", 16777215 /*0XFFFFFF*/);
::printf("|%#7x|\n", 16777215 /*0XFFFFFF*/);
::printf("|%-#8o|\n", 262143 /* 0777777 */);
::printf("|%010d|\n", 123456);
::printf("|%-010d|\n", 123456);
::printf("|%010x|\n", 16777215 /*0XFFFFFF*/);
::printf("|%0#10X|\n", 16777215 /*0XFFFFFF*/);
::printf("|%0#8o|\n", 262143 /* 0777777 */);
/*
输出结果:
| -123456| - 无宽度限制时输出 -123456,字符宽度为 7,小于 width 字段指定的字符宽度 10,flags 字段未指定“-”所以默认右对齐,-123456 前面有三个空格
| 123456| - 无宽度限制时输出 123456,字符宽度为 6,小于 width 字段指定的字符宽度 10,flags 字段未指定“-”所以默认右对齐,123456 前面有四个空格
| ffffff| - 无宽度限制时时输出 ffffff,字符宽度为 6,小于 width 字段指定的字符宽度 7,flags 字段未指定“-”所以默认右对齐,ffffff 前面有一个空格
|0xffffff| - 无宽度限制时输出 0xffffff,字符宽度为 8,大于 width 字段指定的字符宽度 7,因此原样显示
|0777777 | - 无宽度限制时输出 0777777,字符宽度为 7,小于 width 字段指定的字符宽度 8,又因为指定了 “-” 强制左对齐,所以 0777777 左对齐、后面有一个空格
|0000123456| - 无宽度限制时输出 123456,字符宽度为 6,小于 width 字段指定的字符宽度 10,flags 字段未指定“-”所以默认右对齐,flags 字段指定了“0”,因此添加前导 0
|1234560000| - 无宽度限制时输出 123456,字符宽度为 6,小于 width 字段指定的字符宽度 10,flags 字段指定了“-”所以左对齐对齐,flags 字段指定了“0”,但由于本身左对齐、无法再添加前导 0
|0000ffffff| - 无宽度限制时时输出 ffffff,字符宽度为 6,小于 width 字段指定的字符宽度 10,flags 字段未指定“-”所以默认右对齐,flags 字段指定了“0”,因此添加前导 0
|0X00FFFFFF| - 无宽度限制时时输出 ffffff,字符宽度为 6,小于 width 字段指定的字符宽度 10,flags 字段未指定“-”所以默认右对齐,flags 字段指定了“0”,因此添加前导 0,注意对于带有进制前缀的数、前导 0 需要添加在进制前缀后面
|00777777| - 无宽度限制时输出 0777777,字符宽度为 7,小于 width 字段指定的字符宽度 8,flags 字段未指定“-”所以默认右对齐,flags 字段指定了“0”,因此添加前导 0
*/
// 3. 长度
::printf("%hhd\n", 127);
::printf("%hhd\n", 128);
::printf("%hhu\n", 255);
::printf("%hhu\n", 256);
/*
输出结果:
127 - char 类型的整数可以取 127,能正常输出
-128 - char 类型的整数不可以取 128,不能正常输出,事实上,这属于未定义行为,输出结果是不确定的(不一定是 -128)
255 - unsigned char 类型的整数可以取 255
0 - unsigned char 类型的整数不可以取 256,不能正常输出,事实上这属于未定义行为,输出结果是不确定的(不一定是 0)
*/
return 0;
}
」
]
3) 与小数相关的格式化占位符:
①小数相关的格式化占位符分类:[i:与浮点数相关的占位符可以分为以下三类
>a. 定点表示法占位符:%f;
>b. 指数表示法占位符:%e、%E;
>c. 通用格式占位符:%g、%G;
]
②定点表示法占位符默认输出格式:[i:定点表示法占位符 %f 的默认输出格式如下][b:
[-]dd···d.dddddd
// 源数据为正数时无符号位(注意并不是“有符号位只是不显示正号”),为负数时有符号位且为负号
// 整数部分位数不做限制
// 默认精度为 6(即小数部分位数为 6 位),源数据小数位不足 6 位补 0、超过 6 位则四舍五入到第 6 位
「i:例如」「b:
::printf("%f", 42); // 42.000000
::printf("%f", 3.14); // 3.140000
::printf("%f", 0.5E+02); // 50.000000
::printf("%f", 1.23456789); // 1.234568
::printf("%f", -7.8e-03); // -0.007800
::printf("%f", 123456.7); // 123456.700000
」
]
③定点表达法占位符输出格式控制:[qb:
问题一:定点表达法占位符输出格式控制的语法格式、每个字段的基本含义;
问题二:源数据实际字符宽度与最小宽度不一致时会怎样;
问题三:源数据实际精度与目标精度不一致时会怎样;
问题四:精度指定为 0 时会怎样;
][b:答案:
%[flags][width].[precision][length]f
/*
* flags:
* +/空格:源数据为正数时显示正号/空格;可缺省,默认正数时没有符号位,仅负数时显示负号;
* -:%f 默认输出格式的实际字符宽度小于 width 字段指定的字符宽度时左对齐;可缺省,默认右对齐;
* *:对输出格式控制无效;
* 0:使用前导 0,可缺省,默认不使用;具体来说,%f 默认输出格式的实际字符宽度小于 width 字段指定的字符宽度且对其方式为右对齐时用 0 填充空位而非空格;
* #:对定点表示法占位符无效;
* width:
* 最小字符宽度;可缺省,默认为 0;
* %f 默认输出格式的实际字符宽度小于 width 字段指定的字符宽度时左对齐或右对齐(具体是左对齐还是右对齐取决于 flags 字段)并使用指定字符填充空位(具体使用什么字符填充取决于 flags 字段);
* %f 默认输出格式的实际字符宽度大于 width 字段指定的字符宽度时该字段不起作用;
* precision:
* 精度,即小数位数;可缺省,默认为 6;
* 源数据的精度小于该字段指定的精度时在小数部分末尾填充指定字符(具体填充什么字符取决于 flags 字段)直到小数部分足够 6 位;
* 源数据的精度大于该字段指定的精度时对小数部分第七位做四舍五入;
* 精度指定为 0 时对第一位小数位做四舍五入、最终只保留整数位;
* length:只能为 l,与缺省时等价;
*/
「i:例如」「b:
#include
#include
int main(int argc, char** argv) {
// 加号与空格
::printf("%f\n", 123.456);
::printf("%+f\n", 123.456); // 有符号位,显示正号
::printf("% f\n", 123.456); // 有符号位,显示空格
std::cout << std::endl;
// 减号、前导 0 与宽度
::printf("|%15f|\n", 123.456); // 15 字符宽度,右对齐
::printf("|%-15f|\n", 123.456); // 15 字符宽度,左对齐
::printf("|%-015f|\n", 123.456); // 左对齐情况下使用前导 0 也是无效的
::printf("|%015f|\n", 123.456); // 15 字符宽度,右对齐,使用前导 0
::printf("|%8f|\n", 123.456); // 123.456000 字符宽度为 10,10>8,所以原样显示
std::cout << std::endl;
// 精度
::printf("%f\n", 123.123456789); // 默认精度为 6
::printf("%.2f\n", 123.123456789); // 精度降低,对第 3 位 3 进行四舍五入
::printf("%.7f\n", 123.123456789); // 精度提高,对第 8 位 8 进行四舍五入
::printf("%.0f\n", 123.456); // 精度为 0,对第一位小数 4 进行四舍五入
::printf("%.0f\n", 123.678); // 精度为 0,对第一位小数 6 进行四舍五入
::printf("%.0f\n", 123.567); // 精度为 0,对第一位小数 5 进行四舍五入
::printf("%.0f\n", 124.567); // 精度为 0,对第一位小数 5 进行四舍五入
std::cout << std::endl;
// 综合案例
::printf("|%+020.10f|\n", 123.456); // 有符号位并显示正号、右对齐并使用前导 0、字符宽度最小为 20、精度为 10
::printf("|%+-20.10f|\n", 123.456); // 有符号位并显示正号、左对齐、字符宽度最小为 20、精度为 10
return 0;
}
/*
输出结果:
123.456000
+123.456000
123.456000
| 123.456000|
|123.456000 |
|123.456000 |
|00000123.456000|
|123.456000|
123.123457
123.12
123.1234568
123
124
124
125
|+00000123.4560000000|
|+123.4560000000 |
*/
」
]
④定点表示法占位符默认输入格式:[i:定点表示法占位符 %f 会从输入缓冲区中的对应位置尝试匹配满足以下格式的数据
>符号位(可选);
>整数部分;
>小数点和小数部分(可选);
>指数部分(可选,指数标志大小写和指数位数不做限制,指数可以包含前导 0);
>能匹配的数的取值范围为 float 单精度浮点数的取值范围;「i:例如」
][b:「b:
#include
#include
int main(int argc, char** argv) {
float f;
int res = ::scanf("%f", &f);
::printf("res: %d, f: %f\n", res, f);
return 0;
}
// 输入 12,匹配成功(res:1, f: 12.000000)
// 输入 -1.2,匹配成功(res: 1, f: -1.200000)
// 输入 -123.456E-02,匹配成功(res: 1, f: -1.234560)
// 输入 abc123,匹配失败
」]
⑤定点表示法占位符输入格式控制:[i:定点表示法占位符输入格式控制的语法格式如下][b:
%[flags][width][length]f
/*
* flags:只能为“*”,表示忽略输入缓冲区中对应位置的一个数;可缺省,默认不忽略;
* width:输入字符宽度;可缺省,默认为 %f 能成功匹配的字符数;
* 当源数据字符宽度大于该字段指定的字符宽度时,源数据被截断、只输入后者对应的字符数;
* 当源数据字符宽度小于等于该字段指定的字符宽度时,源数据及其后面的若干字符(width - 源数据字符宽度)会被输入到输入缓冲区,随后源数据被成功匹配从而被提取,但是其后的若干字符会滞留在输入缓冲区中,这可能影响后续的输入行为;
* length:只能为 l,表示能成功匹配的数据的取值范围为 double 双精度浮点型数据的取值范围;可缺省,默认为 float 单精度浮点型数据的取值范围;
*/
「i:例如」「b:
float f;
int res1, res2, n = 666;
char buf[32] = {0};
::memset(buf, 0, sizeof(buf));
res1 = ::scanf("%7f%d%s", &f, &n, buf);
::printf("res1: %d, f: %f, n: %d, buf: %s\n", res1, f, n, buf);
/*
* 输入 123.456abc123:
* a. %7f 会将 123.456 输入到输入缓冲区中,随后它会被成功匹配并提取到变量 f 中;
* b. 'a' 会被输入到输入缓冲区中,%d 无法匹配该字符,匹配失败,匹配停止;
* c. 由于匹配停止,%s 不会匹配任何内容;
* 因此输出结果为 res1: 1, f: 123.456000, n: 666, buf:
*
* 输入 123.4567abc123:
* a. %7f 会将 123.456 输入到输入缓冲区中,随后它会被成功匹配并提取到变量 f 中;
* b. '7' 会被输入到输入缓冲区中,%d 能匹配该字符;
* c. 'a' 会被输入到输入缓冲区中,%d 不能匹配该字符;
* d. %s 能匹配 'a',随后继续依次输入各个字符 'b'、'c'、'1'、'2'、'3'、回车,回车前的所有字符 %s 都能匹配而被提取到 buf 中,回车是空白字符,%s 终止匹配;
* 因此输出结果为 res1: 3, f: 123.456000, n: 7, buf: abc123
*
* 输入 123.45abc123:
* a. %7f 会将 123.45a 输入到输入缓冲区中,随后 123.45 会被成功匹配并提取到变量 f 中;
* b. %d 无法匹配字符 'a',匹配失败,匹配停止;
* c. 由于匹配停止,%s 不会匹配任何内容;
* 因此输出结果为 res1: 1, f: 123.450000, n: 666, buf:
*/
::memset(buf, 0, sizeof(buf));
res2 = ::scanf("%10f%s", &f, &n, buf);
::printf("res2: %d, f: %f, buf: %s\n", res2, f, n, buf);
/*
* 输入 123.456abc123:
* a. %10f 会将 123.456abc 输入到输入缓冲区中,随后 123.456 它会被成功匹配并提取到变量 f 中;
* b. %s 可以匹配输入缓冲区滞留的 "abc";
* c. 匹配完字符 'c' 后,继续依次将 '1'、'2'、'3'、回车输入到输入缓冲区;
* d. %s 能匹配回车前的所有字符,因此 "abc123" 被提取到 buf 中,回车是空白字符,%s 终止匹配;
* 因此输出结果为 res2: 2, f: 123.456000, buf: abc123;从此示例可以看出,由于输入缓冲区的存在与字符的滞留,导致最后 buf 的输出结果为 abc123 而非只有 123;
*/
」
]
⑥指数表示法占位符默认输出格式:[i:指数表达式法的类型占位符的默认格式为如下形式的科学计数法][b:
%e: [-]d.dddddde[±]dd
%E: [-]d.ddddddE[±]dd
// 源数据为正数时没有符号位,为负数时有符号位并显示负号
// 小数点前只有一位整数,取值范围为 [1,10)
// 默认精度为 6(小数位为 6 位,因此有效数字为 7 位),小数位不足 6 位补 0、超过 6 位做四舍五入
// 指数位最少为 2 位
「i:例如」「b:
::printf("%e\n", 1234567890.0); // 1.234568e+09
::printf("%e\n", 123456.789); // 1.234568e+05
::printf("%e\n", 123.450000); // 1.234500e+02
::printf("%E\n", -0.000123456); // -1.234560E-04
::printf("%E\n", 0.000000123); // 1.230000E-07
」
]
⑦指数表示法占位符输出格式控制:[qb:
问题一:指数表示法法占位符输出格式控制的语法格式、每个字段的基本含义;
问题二:源数据实际字符宽度与最小宽度不一致时会怎样;
问题三:源数据实际精度与目标精度不一致时会怎样;
问题四:精度指定为 0 时会怎样;
][b:答案:
%[flags][width].[precision][length]e/E
/*
* flags:
* +/空格:源数据为正数时显示正号/空格;可缺省,默认正数时没有符号位,仅负数时显示负号;
* -:%e/%E 默认格式的实际字符宽度小于 width 字段指定的字符宽度时左对齐;可缺省,默认右对齐;
* *:对格式化输出无效;
* 0:0:使用前导 0,可缺省,默认不使用;具体来说,%e/%E 默认输出格式的实际字符宽度小于 width 字段指定的字符宽度且对其方式为右对齐时用 0 填充空位而非空格;
* #:对科学计数法无效;
* width:
* 最小字符宽度;可缺省,默认为 0;
* %e/%E 默认格式的实际字符宽度小于 width 字段指定的最小宽度时左对齐或右对齐(具体是左对齐还是右对齐取决于 flags 字段)并使用指定字符填充空位(具体使用什么字符填充取决于 flags 字段);
* %e/%E 默认格式的实际字符宽度大于 width 字段指定的字符宽度时该字段不起作用;
* precision:
* 精度,即小数位数;可缺省,默认为 6;
* %e/%E 默认格式的实际精度小于该字段指定的精度时在小数部分末尾填充 0 直到小数部分足够 6 位;
* %e/%E 默认格式的实际精度大于该字段指定的精度时对小数部分第 precsion + 1 位做四舍五入;
* 精度指定为 0 时对 %e/%E 默认格式的第一位小数做四舍五入;
* length:
* 长度,只能为 l,与缺省时等价;
* e/E:
* e:指数标志 e 小写;
* E:指数标志 E 大写;
*/
「i:例如」「b:
#include
#include
int main(int argc, char** argv) {
// 空格与“+”
::printf("|%e|\n", 123.456); // 原样输出
::printf("|%+e|\n", 123.456); // 相比原样输出多了符号位并显示正号
::printf("|% e|\n", 123.456); // 相比原样输出多了符号位并显示空格
std::cout << std::endl;
// 负号、0 与宽度
::printf("|%20e|\n", 123.456); // 字符宽度为 20、右对齐、空位为空格
::printf("|%-20e|\n", 123.456); // 字符宽度为 20、左对齐、空位为空格
::printf("|%020e|\n", 123.456); // 字符宽度为 20、右对齐、空位为 0
::printf("|%-020e|\n", 123.456); // 字符宽度为 20、左对齐、空位为空格(0 无效)
::printf("|%7e|\n", 123.456); // 由于 7 小于 %e 默认格式的字符宽度(1.234560e+02 共 12 个字符),因此原样显示
std::cout << std::endl;
// 精度
::printf("|%.4e|\n", 123.456); // 4 小于 %e/%E 默认格式的精度 6,小数位第 5 位会进行四舍五入
::printf("|%.8e|\n", 123.456); // 8 大于 %e/%E 默认格式的精度 6,小数位末尾会补 0 直到小数位为 8 位
::printf("|%.0e|\n", 145.678); // 精度为 0,对 %e/%E 默认格式的第一位小数位 4 进行四舍五入
::printf("|%.0e|\n", 166.789); // 精度为 0,对 %e/%E 默认格式的第一位小数位 6 进行四舍五入
::printf("|%.0e|\n", 156.789); // 精度为 0,对 %e/%E 默认格式的第一位小数位 5 进行四舍五入
::printf("|%.0e|\n", 256.789); // 精度为 0,对 %e/%E 默认格式的第一位小数位 5 进行四舍五入
std::cout << std::endl;
// 综合案例
::printf("|%0+20.8e|\n", 123.456);
::printf("|%0+20.4e|\n", 123.456);
return 0;
}
/*
输出结果:
|1.234560e+02|
|+1.234560e+02|
| 1.234560e+02|
| 1.234560e+02|
|1.234560e+02 |
|000000001.234560e+02|
|1.234560e+02 |
|1.234560e+02|
|1.2346e+02|
|1.23456000e+02|
|1e+02|
|2e+02|
|2e+02|
|3e+02|
|+000001.23456000e+02|
|+0000000001.2346e+02|
*/
」
]
⑧指数表示法占位符默认输入格式:[i:%e/%E 会从输入缓冲区中的对应位置尝试匹配满足以下格式的数据
>符号位(可选);
>整数部分;
>小数点和小数部分(可选);
>指数部分(可选,指数标志大小写和指数位数不做限制,指数部分可以包含前导 0);
>输入时 %e 和 %E 等价;
>能匹配的数的取值范围为 float 单精度浮点数的取值范围;「i:例如」
][b:「b:
#include
#include
int main(int argc, char** argv) {
float f;
int res = ::scanf("%E", &f);
::printf("res: %d, f: %e\n", res, f);
return 0;
}
// 输入 12.3,匹配成功(res:1, f: 1.230000e+01)
// 输入 -1.2E+2,匹配成功(res: 1, f: -1.200000e+02)
// 输入 123456E+003,匹配成功(res: 1, f: 1.234560e+08)
」]
⑨指数表示法占位符输入格式控制:[i:指数表示法占位符默认输入格式如下][b:
%[flags][width][length]e/E
/*
* flags:只能为“*”,表示忽略输入内容对应位置的一个数;可缺省,默认不忽略;
* width:输入字符宽度;可缺省,默认输入全部能匹配的字符;
* 当源数据字符宽度大于该字段指定的字符宽度时,源数据被截断、只输入后者对应的字符数;
* 当源数据字符宽度小于等于该字段指定的字符宽度时,源数据以及它后面的若干字符(width - 源数据字符宽度)会被输入到输入缓冲区,随后源数据被成功匹配从而被提取、但其后的若干字符会滞留在输入缓冲区,这可能对后续输入行为产生影响;
* length:只能为 l,表示能成功匹配的数据的取值范围为 double 双精度浮点数的取值范围;可缺省,默认能成功匹配的数据的取值范围为 float 单精度浮点数的取值范围;
* e/E:
* e:匹配小写的指数标志;
* E:匹配大写的指数标志;
*/
「i:例如」「b:
float f;
int res1 = ::scanf("%10e", &f);
::printf("res1: %d, f: %e\n", res1, f);
// 输入 123.456E+03,由于它的字符宽度为 11,而指定的输入字符宽度为 10,因此数据被截断,只能输入 123.456E+0(输出:res1: 1, f: 1.234560e+02)
char buf[16] = {0};
int res2 = ::scanf("%14e%s", &f, buf);
::printf("res2: %d, f: %e, buf: %s\n", res2, f, buf);
// 输入 123.456E+03abc123,由于其中的数字的字符宽度为 11,而指定的输入字符宽度为 14,因此数据及其后面的“abc”被输入到输入缓冲区,随后前面的数字被成功匹配从而被提取,而“abc”则滞留在输入缓冲区中,在匹配 %s 时,由于输入缓冲区中仍有内容,所以会先提取输入缓冲区中的“abc”,由于在匹配过程中没有遇到空白字符,所以提取完字符 'c' 之后会继续将剩余的字符依次送入输入缓冲区并进行匹配,当匹配完字符 '3' 之后,遇到回车,结束匹配,所以最后的输出u结果为:res2: 1, f: 1.234560e+05, buf: abc123,即 buf 为 abc123 而非 123
」
]