PRINTF - Linux手册页
Linux程序员手册 第3部分
更新日期: 2020-06-09
名称
printf,fprintf,dprintf,sprintf,snprintf,vprintf,vfprintf,vdprintf,vsprintf,vsnprintf-格式化输出转换
语法
#include <stdio.h> int printf(const char *format, ...); int fprintf(FILE *stream, const char *format, ...); int dprintf(int fd, const char *format, ...); int sprintf(char *str, const char *format, ...); int snprintf(char *str, size_t size, const char *format, ...); #include <stdarg.h> int vprintf(const char *format, va_list ap); int vfprintf(FILE *stream, const char *format, va_list ap); int vdprintf(int fd, const char *format, va_list ap); int vsprintf(char *str, const char *format, va_list ap); int vsnprintf(char *str, size_t size, const char *format, va_list ap);
glibc的功能测试宏要求(请参阅feature_test_macros(7)):
snprintf(),vsnprintf():
- _XOPEN_SOURCE>= 500 || _ISOC99_SOURCE || || / * Glibc版本
dprintf(),vdprintf():
- Since glibc 2.10:
- _POSIX_C_SOURCE>= 200809L
- Before glibc 2.10:
- _GNU_SOURCE
说明
printf()系列中的函数根据如下所述的格式产生输出。函数printf()和vprintf()将输出写入标准输出流stdout。 fprintf()和vfprintf()将输出写入给定的输出流; sprintf(),snprintf(),vsprintf()和vsnprintf()写入字符串str。
函数dprintf()与fprintf()相同,只不过它输出到文件描述符fd而不是stdio流。
函数snprintf()和vsnprintf()最多将大小字节(包括终止的空字节(aq \ 0aq))写入str。
函数vprintf(),vfprintf(),vdprintf(),vsprintf(),vsnprintf()分别等效于函数printf(),fprintf(),dprintf(),sprintf(),snprintf()。使用va_list而不是可变数量的参数来调用它们。这些函数不调用va_end宏。由于它们调用va_arg宏,因此在调用之后ap的值是不确定的。参见stdarg(3)。
所有这些函数都在格式字符串的控制下写入输出,该格式字符串指定如何转换后续参数(或通过stdarg(3)的可变长度参数工具访问的参数)以输出。
C99和POSIX.1-2001指定如果对sprintf(),snprintf(),vsprintf()或vsnprintf()的调用将导致在重叠的对象之间进行复制(例如,如果目标字符串),则结果是不确定的数组和提供的输入参数之一引用相同的缓冲区)。请参阅注释。
Format of the format string
格式字符串是一个字符串,以其初始移位状态(如果有)开始和结束。格式字符串由零个或多个指令组成:普通字符(不是%),原样复制到输出流;和转换规范,每个规范都会导致获取零个或多个后续参数。每个转换说明都以字符%开头,并以转换说明符结尾。在两者之间可能有(按此顺序)零个或多个标志,可选的最小字段宽度,可选的精度和可选的长度修饰符。
参数必须与转换说明符正确对应(在类型提升之后)。默认情况下,参数以给定的顺序使用,其中每个aq * aq(请参见下面的"字段宽度"和"精度")和每个转换说明符都要求下一个参数(如果给出的参数太多,则会出错)。通过写"%m $"而不是aq%aq和" * m $"而不是aq * aq,还可以明确指定在需要参数的每个位置采用哪个参数,其中十进制整数m表示所需参数的参数列表中的位置,从1开始索引。因此,
printf("%*d", width, num);
和
printf("%2$*1$d", width, num);
是等效的。第二种样式允许重复引用相同的参数。 C99标准不包括来自Single UNIX规范的使用aq $ aq的样式。如果使用使用aq $ aq的样式,则必须在所有带参数的转换以及所有宽度和精度参数中使用该样式,但是它可以与不使用参数的" %%"格式混合使用。使用aq $ aq指定的参数数量可能没有空格。例如,如果指定了参数1和3,则还必须在格式字符串的某处指定参数2。
对于某些数字转换,将使用基数字符("小数点")或数千个分组字符。实际使用的字符取决于语言环境的LC_NUMERIC部分。 (请参阅setlocale(3)。)POSIX语言环境使用aq.aq作为基数字符,并且没有分组字符。从而,
printf("%aq.2f", 1234567.89);
在POSIX语言环境中为" 1234567.89",在nl_NL语言环境中为" 1234567,89",在da_DK语言环境中为" 1.234.567,89"。
Flag characters
字符%后跟零个或多个以下标志:
- #
- 该值应转换为"替代形式"。对于o转换,将输出字符串的第一个字符设置为零(如果还不为零,则将其前缀为0)。对于x和X转换,非零结果前面带有字符串" 0x"(对于X转换,则为" 0X")。对于a,a,e,e,f,f,g和G转换,即使没有数字跟随,结果也始终包含小数点(通常,只有当a,位数字)。对于g和G转换,不会从结果中删除尾随零,否则会从中去除。对于其他转换,结果是不确定的。
- 0
- 该值应为零填充。对于d,i,o,u,x,x,a,a,e,e,f,f,g和G转换,转换后的值在左侧填充零而不是空白。如果同时出现0和-标志,则忽略0标志。如果通过数字转换(d,i,o,u,x和X)给出精度,则忽略0标志。对于其他转换,行为是不确定的。
- -
- 转换后的值将在场边界上进行左调整。 (默认值为右对齐。)转换后的值在右侧用空格填充,而不是在左侧用空格或零填充。如果同时给出-,则-会覆盖0。
- aq aq
- (空格)在带符号的转换产生的正数(或空字符串)之前应留一个空格。
- +
- 应始终在带符号的转换产生的数字之前放置一个符号(+或-)。默认情况下,符号仅用于负数。如果同时使用+,则+会覆盖空格。
上面的五个标志字符在C99标准中定义。单一UNIX规范还指定了另一个标志字符。
- aq
- 对于十进制转换(i,d,u,f,f,g,G),如果语言环境信息指示任何输出,则输出将与数千个分组字符分组。 (请参阅setlocale(3)。)请注意,许多版本的gcc(1)无法解析此选项,并且会发出警告。 (SUSv2不包括%aqF,但是SUSv3添加了它。)
glibc 2.2添加了另一个标志字符。
- I
- 对于十进制整数转换(i,d,u),输出使用语言环境的替代输出数字(如果有)。例如,由于glibc 2.2.3,这将在波斯语(" fa_IR")语言环境中给出阿拉伯语-印度数字。
Field width
可选的十进制数字字符串(第一位非零)指定最小字段宽度。如果转换后的值的字符数少于字段宽度,它将在左边(或右边,如果已给定了左调整标志)的位置上填充空格。代替十进制数字字符串,可以写" *"或" * m $"(对于某些十进制整数m)来指定字段宽度分别在下一个参数或第m个参数中给出,这必须属于int类型。负场宽度用作aq-aq标志,后跟正场宽度。在任何情况下,不存在或较小的字段宽度都不会导致字段被截断;如果转换结果比字段宽度宽,则将字段扩展为包含转换结果。
Precision
可选精度,以句点(aq.aq)形式,后跟可选的十进制数字字符串。代替十进制数字字符串,可以写" *"或" * m $"(对于某些十进制整数m)来指定精度分别在下一个参数或第m个参数中给出。类型为int。如果给定的精度仅为aq.aq,则精度为零。负精度就像忽略精度一样。这给出了d,i,o,u,x和X转换出现的最小位数,a,a,e,e,f和F转换的基数字符后出现的位数, g和G转换的最大有效位数,或s和S转换的字符串中要打印的最大字符数。
Length modifier
在此,"整数转换"代表d,i,o,u,x或X转换。
- hh
- 后面的整数转换对应于有符号的char或无符号的char参数,或者后面的n转换对应于有符号的char参数的指针。
- h
- 后续的整数转换对应于short int或unsigned short int参数,或者后续的n转换对应于short int参数的指针。
- l
- (ell)后续的整数转换对应于long int或unsigned long int参数,或者后续的n转换对应于long int参数的指针,或者后续的c转换对应于wint_t参数,或者后续的s转换对应指向wchar_t参数的指针。
- ll
- (好吧)。后续的整数转换对应于long long int或unsigned long long int参数,或者后续的n转换对应于long long int参数的指针。
- q
- ll的同义词。这是源自BSD的非标准扩展;避免在新代码中使用它。
- L
- 在a,a,e,e,f,f,g或G转换之后的是一个长double自变量。 (C99允许%LF,但SUSv2不允许。)
- j
- 后面的整数转换对应于intmax_t或uintmax_t参数,或者后面的n转换对应于指向intmax_t参数的指针。
- z
- 后面的整数转换对应于size_t或ssize_t参数,或者后面的n转换对应于指向size_t参数的指针。
- Z
- z的非标准同义词,早于z的出现。不要在新代码中使用。
- t
- 后续的整数转换对应于ptrdiff_t参数,或者后续的n转换对应于指向ptrdiff_t参数的指针。
SUSv3指定以上所有内容,但明确指出是非标准扩展的那些修饰符除外。 SUSv2仅指定长度修饰符h(在hd,hi,ho,hx,hx,hn中)和l(在ld,li,lo,lx,lx,ln,lc,ls中)和l(在Le,Le,Lf中) ,Lg,LG)。
作为非标准扩展,GNU实现将ll和L视为同义词,因此可以例如编写llg(作为符合标准的Lg的同义词)和Ld(作为符合标准的lld的同义词)。这种用法是不可移植的。
Conversion specifiers
一个字符,指定要应用的转换类型。转换说明符及其含义是:
- d, i
- int参数将转换为带符号的十进制表示法。精度(如果有的话)给出了必须出现的最小位数。如果转换后的值需要较少的数字,则在左边用零填充。默认精度为1。以显式精度0打印0时,输出为空。
- o, u, x, X
- 无符号int参数将转换为无符号八进制(o),无符号十进制(u)或无符号十六进制(x和X)表示法。字母abcdef用于x转换;字母ABCDEF用于X转换。精度(如果有的话)给出了必须出现的最小位数。如果转换后的值需要较少的数字,则在左边用零填充。默认精度为1。以显式精度0打印0时,输出为空。
- e, E
- 将double参数四舍五入并转换为[-] d样式。 ddd e±dd,其中小数点字符前有一位数字(如果参数为非零,则为非零),其后的位数与精度相等;如果精度丢失,则取为6;如果精度为零,则不显示小数点字符。 E转换使用字母e(而不是e)来引入指数。指数始终至少包含两位数字;如果值为零,则指数为00。
- f, F
- 将double参数四舍五入并转换为[-] ddd格式的十进制表示法。 ddd,其中小数点后的位数等于精度规格。如果缺少精度,则取为6;否则,取值为0。如果精度明确为零,则不显示小数点字符。如果出现小数点,则在其前面至少出现一位数字。
- (SUSv2不了解F,并说可以提供无穷大和NaN的字符串表示形式。SUSv3添加了F的规范。C99标准为无穷大指定了[[]] inf或" [-] infinity",并且如果是f转换,则以" nan"为NaN开头的字符串,如果是F转换,则以" [-] INF"或" [-] INFINITY"或" NAN"开头。)
- g, G
- double参数将转换为f或e样式(对于G转换,则转换为F或E)。精度指定有效位数。如果缺少精度,则给出6位数字。如果精度为零,则将其视为1。如果转换后的指数小于-4或大于或等于精度,则使用样式e。尾随零从结果的分数部分中删除;只有在小数点后跟至少一位数字时,小数点才会出现。
- a, A
- (C99;不是在SUSv2中,而是在SUSv3中添加的。)为了进行转换,将double参数转换为[-] 0x h样式的十六进制表示法(使用字母abcdef)。 hhhh p±;对于A转换,使用前缀0x,字母ABCDEF和指数分隔符P。小数点前有一个十六进制数字,其后的位数等于精度。如果存在以2为底的精确表示形式,则默认精度足以满足该值的精确表示形式;否则,该缺省精度足够大以区分double类型的值。对于未归一化的数字,未指定小数点前的数字;对于归一化数字,未指定小数点之前的数字。
- c
- 如果不存在l修饰符,则将int参数转换为无符号字符,并写入结果字符。如果存在l修饰符,则通过调用wcrtomb(3)函数将wint_t(宽字符)参数转换为多字节序列,转换状态从初始状态开始,并写入结果的多字节字符串。
- s
- 如果不存在l修饰符:则const char *参数应该是指向字符类型数组的指针(指向字符串的指针)。数组中的字符被写到(但不包括)终止空字节(aq \ 0aq);如果指定了精度,则最多写入指定的数字。如果给出精度,则不需要空字节。如果未指定精度或大于数组的大小,则数组必须包含一个终止的空字节。
- 如果存在l修饰符:则const wchar_t *参数应为指向宽字符数组的指针。数组中的宽字符被转换为多字节字符(每个通过调用wcrtomb(3)函数,转换状态从第一个宽字符之前的初始状态开始),直至并包括一个终止的空宽字符。产生的多字节字符被写入(但不包括)终止空字节。如果指定了精度,则写入的字节数不超过指定的数字,但不写入部分多字节字符。请注意,精度决定写入的字节数,而不是宽字符或屏幕位置的数目。除非给出精度,否则数组必须包含一个终止的null宽字符,该精度太小以至于在到达数组末尾之前写入的字节数超过了它。
- C
- (不在C99或C11中,但在SUSv2,SUSv3和SUSv4中。)lc的同义词。不要用
- S
- (不在C99或C11中,但在SUSv2,SUSv3和SUSv4中。)ls的同义词。不要用
- p
- void *指针参数以十六进制打印(就像用%#x或%#lx一样)。
- n
- 到目前为止写入的字符数存储在相应参数所指向的整数中。该参数应为int *或其大小与(可选)提供的整数长度修饰符匹配的变体。没有参数被转换。 (仿生C库不支持此说明符。)如果转换说明包含任何标志,字段宽度或精度,则该行为不确定。
- m
- (Glibc扩展;由uClibc和musl支持。)打印strerror(errno)的输出。不需要参数。
- %
- 会写一个aq%aq。没有参数被转换。完整的转换规范为aq %% aq。
返回值
成功返回后,这些函数将返回打印的字符数(不包括用于结束输出到字符串的空字节)。
函数snprintf()和vsnprintf()写入的字节数不超过大小字节(包括终止的空字节(aq \ 0aq))。如果由于此限制而输出被截断,则返回值是如果有足够的空间,将被写入最终字符串的字符数(不包括终止的空字节)。因此,size或更大的返回值意味着输出被截断。 (另请参见下面的注释。)
如果遇到输出错误,则返回负值。
属性
有关本节中使用的术语的说明,请参见attribute(7)。
Interface | Attribute | Value |
printf(),fprintf(), sprintf(),snprintf(), vprintf(),vfprintf(), vsprintf(),vsnprintf() | Thread safety | MT-Safe locale |
遵循规范
fprintf(),printf(),sprintf(),vprintf(),vfprintf(),vsprintf():POSIX.1-2001,POSIX.1-2008,C89,C99。
snprintf(),vsnprintf():POSIX.1-2001,POSIX.1-2008,C99。
dprintf()和vdprintf()函数最初是GNU扩展,后来在POSIX.1-2008中进行了标准化。
关于snprintf()的返回值,SUSv2和C99彼此矛盾:当使用size = 0调用snprintf()时,SUSv2规定了未指定的返回值,小于1,而在这种情况下C99允许str为NULL,并给出返回值(一如既往)是在输出字符串足够大的情况下要写入的字符数。 POSIX.1-2001及更高版本将其snprintf()的规范与C99对齐。
glibc 2.1添加了长度修饰符hh,j,t和z以及转换字符a和A。
glibc 2.2添加具有C99语义的转换字符F和标志字符I。
备注
一些程序不谨慎地依赖于以下代码
sprintf(buf,"%s一些进一步的文本",buf);
将文本追加到buf。但是,标准明确指出,如果在调用sprintf(),snprintf(),vsprintf()和vsnprintf()时源缓冲区和目标缓冲区重叠,则结果不确定。根据所用gcc(1)的版本以及所用的编译器选项,上述调用不会产生预期的结果。
从glibc 2.1版开始,函数snprintf()和vsnprintf()的glibc实现符合C99标准,即,其行为如上所述。直到glibc 2.0.6,当输出被截断时,它们将返回-1。
BUGS
由于sprintf()和vsprintf()假定使用任意长的字符串,因此调用者必须注意不要溢出实际空间。这通常是无法保证的。请注意,生成的字符串的长度取决于语言环境,并且难以预测。请改用snprintf()和vsnprintf()(或asprintf(3)和vasprintf(3))。
诸如printf(foo);的代码通常表示错误,因为foo可能包含%字符。如果foo来自不受信任的用户输入,则它可能包含%n,从而导致printf()调用写入内存并创建安全漏洞。
示例
要将Pi打印到小数点后五个位:
#include <math.h> #include <stdio.h> fprintf(stdout, "pi = %.5f\n", 4 * atan(1.0));
以" 7月3日,星期日,10:02"格式打印日期和时间,其中工作日和月份是指向字符串的指针:
#include <stdio.h> fprintf(stdout, "%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min);
许多国家/地区使用日-月-年顺序。因此,国际化版本必须能够按格式指定的顺序打印参数:
#include <stdio.h> fprintf(stdout, format, weekday, month, day, hour, min);
格式取决于语言环境,并且可能置换参数。带有值:
"%1$s, %3$d. %2$s, %4$d:%5$.2d\n"
一个人可能会获得" Sonntag,3。Juli,10:02"。
要分配足够大的字符串并打印到其中(对于glibc 2.0和glibc 2.1都正确的代码):
#include <stdio.h> #include <stdlib.h> #include <stdarg.h> char * make_message(const char *fmt, ...) { int n = 0; size_t size = 0; char *p = NULL; va_list ap; /* Determine required size */ va_start(ap, fmt); n = vsnprintf(p, size, fmt, ap); va_end(ap); if (n < 0) return NULL; /* One extra byte for '##代码##' */ size = (size_t) n + 1; p = malloc(size); if (p == NULL) return NULL; va_start(ap, fmt); n = vsnprintf(p, size, fmt, ap); va_end(ap); if (n < 0) { free(p); return NULL; } return p; }
如果在2.0.6之前的glibc版本中发生截断,则将其视为错误,而不是进行适当处理。
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。