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