格式化字符串

格式化字符串(英语:format string)是一些程序设计语言的输入/输出中能将字符串参数转换为另一种形式输出的函数。例如C、C++等程序设计语言的printf类函数,其中的转换说明(conversion specification)用于把随后对应的0个或多个函数参数转换为相应的格式输出;格式化字符串中转换说明以外的其它字符原样输出。[1]

一个printf函数的例子

C语言printf函数为代表的一类

格式化占位符

格式化字符串中的占位符用于指明输出的参数值如何格式化。

格式化占位符(format placeholder),语法是:

%[parameter][flags][field width][.precision][length]type

Parameter可以忽略或者是:

字符 描述
n$ n是用这个格式说明符(specifier)显示第几个参数;这使得参数可以输出多次,使用多个格式说明符,以不同的顺序输出。 如果任意一个占位符使用了parameter,则其他所有占位符必须也使用parameter。这是POSIX扩展,不属于ISO C。例:printf("%2$d %2$#x; %1$d %1$#x",16,17) 产生"17 0x11; 16 0x10"

Flags可为0个或多个:

字符 描述
+ 总是表示有符号数值的'+'或'-'号,缺省情况是忽略正数的符号。仅适用于数值类型。
空格 使得有符号数的输出如果没有正负号或者输出0个字符,则前缀1个空格。如果空格与'+'同时出现,则空格说明符被忽略。
- 左对齐。缺省情况是右对齐。
# 对于'g'与'G',不删除尾部0以表示精度。对于'f', 'F', 'e', 'E', 'g', 'G', 总是输出小数点。对于'o', 'x', 'X', 在非0数值前分别输出前缀0, 0x, and 0X表示数制。
0 如果width选项前缀以0,则在左侧用0填充直至达到宽度要求。例如printf("%2d", 3)输出" 3",而printf("%02d", 3)输出"03"。如果0-均出现,则0被忽略,即左对齐依然用空格填充。

Field Width给出显示数值的最小宽度,典型用于制表输出时填充固定宽度的表目。实际输出字符的个数不足域宽,则根据左对齐或右对齐进行填充。实际输出字符的个数超过域宽并不引起数值截断,而是显示全部。宽度值的前导0被解释为0填充标志,如上述;前导的负值被解释为其绝对值,负号解释为左对齐标志。如果域宽值为*,则由对应的函数参数的值为当前域宽。

Precision通常指明输出的最大长度,依赖于特定的格式化类型。对于d、i、u、x、o的整型数值,是指最小数字位数,不足的位要在左侧补0,如果超过也不截断,缺省值为1。对于a,A,e,E,f,F的浮点数值,是指小数点右边显示的数字位数,必要时四舍五入或补0;缺省值为6。对于g,G的浮点数值,是指有效数字的最大位数;缺省值为6。对于s的字符串类型,是指输出的字节的上限,超出限制的其它字符将被截断。如果域宽为*,则由对应的函数参数的值为当前域宽。如果仅给出了小数点,则域宽为0。

Length指出浮点型参数或整型参数的长度。此项Microsoft称为“Size”。可以忽略,或者是下述:

字符 描述
hh 对于整数类型,printf期待一个从char提升的int尺寸的整型参数。
h 对于整数类型,printf期待一个从short提升的int尺寸的整型参数。
l 对于整数类型,printf期待一个long尺寸的整型参数。

对于浮点类型,printf期待一个double尺寸的整型参数。对于字符串s类型,printf期待一个wchar_t指针参数。对于字符c类型,printf期待一个wint_t型的参数。

ll 对于整数类型,printf期待一个long long尺寸的整型参数。Microsoft也可以使用I64
L 对于浮点类型,printf期待一个long double尺寸的整型参数。
z 对于整数类型,printf期待一个size_t尺寸的整型参数。
j 对于整数类型,printf期待一个intmax_t尺寸的整型参数。
t 对于整数类型,printf期待一个ptrdiff_t尺寸的整型参数。

此外,在ISO C99广泛接受前,还有几个平台相关的length选项:

字符 描述
I 对于有符号整数类型,printf期待一个ptrdiff_t尺寸的整型参数。对于无符号整数类型,printf期待一个size_t尺寸的整型参数。常见于Win32/Win64平台。
I32 对于整数类型,printf期待一个32位(双字)的整型参数。常见于Win32/Win64平台。
I64 对于整数类型,printf期待一个64位(四字)的整型参数。常见于Win32/Win64平台。
q 对于整数类型,printf期待一个64位(四字)的整型参数。常见于BSD平台。

ISO C99的头文件inttypes.h包含了许多宏,用于平台独立的printf编码。例如:

定义
PRId32 典型地等效于I32d (Win32/Win64)或d
PRId64 典型地等效于I64d (Win32/Win64), lld (32位平台)或ld (64位平台)
PRIi32 典型地等效于I32i (Win32/Win64)或i
PRIi64 典型地等效于I64i (Win32/Win64), lli (32位平台)或li (64位平台)
PRIu32 典型地等效于I32u (Win32/Win64)或u
PRIu64 典型地等效于I64u (Win32/Win64), llu (32位平台)或lu (64位平台)
PRIx32 典型地等效于I32x (Win32/Win64)或x
PRIx64 典型地等效于I64x (Win32/Win64), llx (32位平台)或lx (64位平台)

类型

Type,也称转换说明(conversion specification/specifier),可以是:

字符 描述
d, i 有符号十进制数值int。'%d'与'%i'对于输出是同义;但对于scanf()输入二者不同,其中%i在输入值有前缀0x或0时,分别表示16进制或8进制的值。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
u 十进制unsigned int。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
f, F double型输出10进制定点英语Fixed-point arithmetic表示。'f'与'F'差异是表示无穷与NaN时,'f'输出'inf', 'infinity'与'nan';'F'输出'INF', 'INFINITY'与'NAN'。小数点后的数字位数等于精度,最后一位数字四舍五入。精度默认为6。如果精度为0且没有#标记,则不出现小数点。小数点左侧至少一位数字。
e, E double值,输出形式为10进制的([-]d.ddd e[+/-]ddd). E版本使用的指数符号为E(而不是e)。指数部分至少包含2位数字,如果值为0,则指数部分为00。Windows系统,指数部分至少为3位数字,例如1.5e002,也可用Microsoft版的运行时函数_set_output_format 修改。小数点前存在1位数字。小数点后的数字位数等于精度。精度默认为6。如果精度为0且没有#标记,则不出现小数点。
g, G double型数值,精度定义为全部有效数字位数。当指数部分在闭区间 [-4,5] 内,输出为定点形式;否则输出为指数浮点形式。'g'使用小写字母,'G'使用大写字母。小数点右侧的尾数0不被显示;显示小数点仅当输出的小数部分不为0。
x, X 16进制unsigned int。'x'使用小写字母;'X'使用大写字母。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
o 8进制unsigned int。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
s 如果没有用l标志,输出null结尾字符串英语null-terminated string直到精度规定的上限;如果没有指定精度,则输出所有字节。如果用了l标志,则对应函数参数指向wchar_t型的数组,输出时把每个宽字符转化为多字节字符,相当于调用wcrtomb函数。
c 如果没有用l标志,把int参数转为unsigned char型输出;如果用了l标志,把wint_t参数转为包含两个元素的wchart_t数组,其中第一个元素包含要输出的字符,第二个元素为null宽字符。
p void *
a, A double型的16进制表示,"[−]0xh.hhhh p±d"。其中指数部分为10进制表示的形式。例如:1025.010输出为0x1.004000p+10。'a'使用小写字母,'A'使用大写字母。[2][3] (C++11流使用hexfloat输出16进制浮点数)
n 不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
% '%'字面值,不接受任何flags, width, precision or length。

宽度与精度格式化参数可以忽略,或者直接指定,或者用星号"*"表示取对应函数参数的值。例如printf("%*d", 5, 10)输出"   10";printf("%.*s", 3, "abcdef") 输出"abc"。

如果函数参数太少,不能匹配所有的格式参数说明符,或者函数参数的类型不匹配,将导致未定义(undefined)行为。过多的函数参数被忽略。许多时候,未定义的行为将导致格式化字符串攻击英语Format string attack

某些编译器,如GCC,会静态检查printf这一类函数的格式化参数并编译警告存在的问题(当使用编译标志-Wall-Wformat)。GCC也会对用户自定义的printf风格函数做静态检查,如果在函数定义时使用了非标准的 __attribute__((format(...)))

具有printf风格函数的编程语言

此外,Windows API的FormatMessage函数也使用格式化字符串说明。

参考文献

  1. ^ C11 standard n1570 draft. 7.21.6.1.3 pp309
  2. ^ "The GNU C Library Reference Manual", "12.12.3 Table of Output Conversions". Gnu.org. [2014-03-17]. (原始内容存档于2021-03-27). 
  3. ^ "printf"页面存档备份,存于互联网档案馆) ("%a" added in C99)

外部链接