man fprintf printf sprintf vfprintf vsprintf

名称

        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):

        snprintf(), vsnprintf():

            _BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE ||

            _POSIX_C_SOURCE >= 200112L;

            或者 cc -std=c99

        dprintf(), vdprintf():

            从glibc 2.10开始:

                _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L

            在glibc 2.10之前:

                _GNU_SOURCE

说明

        printf函数族按照下文描述的格式产生输出。printf和vprintf把输出写到stdout,

        即标准输出流。fprintf和vfprintf把输出写到stream指向的流。sprintf,snprintf,

        vsprintf和vsnprintf把输出写到参数str指向的字符串。

        dprintf等价于fprintf,除了它输出到文件描述符fd而不是标准I/O流之外。

        snprintf和vsnprintf最多写size个字节(包括结束符(’\0’))到str。

        vprintf,vfprintf,vdprintf,vsprintf,vsnprintf分别等价于printf,fprintf,dprintf,

        sprintf,snprintf,除了它们有一个va_list类型的参数而不是可变数量的参数之外。

        这些函数并不调用va_end宏。因为它们调用了va_arg宏,调用后ap的值是未定

        义。参考stdarg。

        所有的这些函数按照参数format的控制来写输出,format指定了后续的参数(或

        者通过stdarg系列工具获取到的可变数量的参数)在输出的过程中怎样做转换。

        C99和POSIX.1-2001规定,如果调用sprintf,snprintf,vsprintf或者vsnprintf引

        起了对象之间重叠的部分发生了拷贝,那么结果是未定义的(例如,目标字符串数

        组和一个输入参数指向了相同的缓冲区)。阅读注意

    格式串的格式

        格式串是一个字符串,以它的初始移位状态开始和结束,如果有的话。格式串由零

        个或多个指令组成:普通字符(不是’%’)原封不动拷贝到输出流,而每条转换规则

        会引发获取后续零个或者多个参数的动作。转换规则由字符’%’开头,由转换说明符

        结束,在它们之间可能会有(按序排列)零个或多个标识符,一个可选的最小字段

        宽度,一个可选的精度值,一个可选的长度修饰符。

        参数必须与转换说明符正确对应(在类型提升之后)。默认情况下,按照出现的顺

        序处理参数,每一个’*’和每一个转换说明符都引发取下一个参数(如果给出的参数

        不够多,就会出错)的动作。在处理参数的时候也可以明确指出要处理哪一个参数,

        这通过用"%m$"代替'%'和"*m$"代替'*',十进制整数m表示被处理的参数在参数列

        表中的位置,从1开始算起。所以,

            printf("%*d", width, num);

            和

            printf("%2$*1$d", width, num);

        是等价的。'$'风格可以重复取到相同的参数。SUS支持'$'风格,C99不

        支持。如果使用了'$'风格的格式,那么同一语句中所有的参数转换都要使用这种格

        式,这时可以和"%%"一起使用,因为"%%"并不会引发取参数的动作。使用'$'风格时,

        参数转换不能有间断,比如参数1和参数3做了格式化转换,那么在格式串中也需

        要对参数2做格式转换。

        一些数字转换会用到基数字符(小数点)和千位分组字符,实际使用哪个字符是由

        本地化语境LC_NUMERIC宏决定的。POSIX本地化语境使用'.'作为基数字符并

        且没有千位分组字符。所以,

                printf("%'.2f", 1234567.89);

        在POSIX本地化语境中打印"1234567.89",在nl_NL本地化语境中打印

        "1234567,89",在da_DK本地化语境中打印"1.234.567,89"。

    标识符

        字符’%’后面可以跟零个或多个标识符:

        #      输出值被转换成一种“替代形式”,对于转换说明符为’o’来说,输出字符串

               的第一个字符是’0’(如果本来不是’0’,那就在前面加’0’)。对于x和X的

               转换,一个非零的结果会有一个"0x"(X转换就是"0X")串在最前面。对于

               a,A,e,E,f,F,g和G的转换,结果中总是会包含一个小数点,尽管

               小数点后面没有数字(一般情况下,只有在小数点后面有数字的时候小数

               点才会出现在结果中)。对于g和G的转换,尾部的零会被删除。对于其

               他的转换,结果是未定义的。

        0      输出值补0,对于d,i,o,u,x,X,a,A,e,E,f,F,g和G 的转

               换,会在输出值的左边补0而不是空格。如果0和-标识同时出现,那么0

               标识被忽略。如果在做数字转换(d,i,o,u,x和X)的时候给出了精确

               度,那么0标识被忽略。对于其他的转换,结果是未定义的。

        -      输出值将在字段边界上进行左对齐。(默认是右对齐的。)会在输出值的

               右边补空格而不是在左边补空格或者0。当同时出现时,-覆盖0。               

        ' '     (一个空格)当有符号的转换产生一个正数(或者空串)时,正数的左边

               会有一个空格。

        +      做有符号的转换时一个符号(加号+或者减号-)总是会出现在数字的前面。

               默认情况下,只有是负数的时候才会出现符号。当同时出现时,+覆盖' '。

        上述5个字符由C99定义,SUS在此之上增加了一个标识符。

        '      对于十进制转换(i,d,u,f,F,g,G),如果有本地化语境的需要,那

               么输出被组织成千位分组的形式。注意,很多版本的gcc不能解析这个选

               项并且会产生一个警告。(SUSv2不支持%'F, 但是SUSv3支持它。)

        glibc 2.2在以上基础之上又增加了一个标识符。

        I      对于十进制转换(i,d,u),如果有本地化语境的需要,那么在输出中使

               用本地化语境输出数字。例如,从glibc 2.2.3开始,在波斯语境中使用阿

               拉伯-印度数字。

    字段宽度

        一个可选的十进制数字串(第一个数字不为0)指定最小字段宽度。如果输出值的

        字符数比字段宽度小,那么在左边填充空格(或者在右边,如果使用了左对齐标示)。

        也可以使用"*"或者"*m$"(m是一个十进制整数)的方式表示字段宽度由下一个参

        数或者第m个参数确定,该参数必须为int类型。一个负数的字段宽度会被当做是

        一个'-'标识后面跟了一个正数的字段宽度。字段宽度不存在或者小的字段宽度在任

        何情况下都不会导致一个字段被截断。如果转换结果的宽度比字段宽度要宽,那该

        字段就被扩展以包含整个转换结果。

    精度值

        一个可选的精度值,以小数点后面跟着一串十进制数字串的形式出现,也可以使用

        "*"或者"*m$"(m是一个十进制整数)的方式表示精度值由下一个参数或者第m个

        参数确定,该参数必须为int类型。如果精度只给出'.',那么精度值会被当做是0。

        一个负数的精度被当做是省略精度值。该值给出了d、i、o、u、x和X转换出现的

        最小位数,a、A、e、E、f和F转换的基数字符后出现的位数,g和G转换的最大

        有效数字数,或s和S转换从字符串打印的最大字符数。

    长度修饰符

        在这部分内容里,“整数转换”代表d,i,o,u,x或X的转换。

        hh     后面跟一个“整数转换”对应一个signed char或者unsigned char参数,或

               者跟一个n的转换对应一个指向signed char参数的指针。

        h      后面跟一个“整数转换”对应一个short int或者unsigned short int参数,或

               者跟一个n的转换对应一个指向short int参数的指针。

        l      后面跟一个“整数转换”对应一个long int或者unsigned long int参数,或者

               跟一个n的转换对应一个指向long int参数的指针,或者跟一个c的转换

               对应一个wint_t参数,或者跟一个s的转换对应一个指向wchar_t参数的

               指针。

        ll     后面跟一个“整数转换”对应一个long long int或者unsigned long long int

              或者跟一个n的转换对应一个指向signed char参数的指针。

        L      后面跟a,A,e,E,f,F,g或者G的转换对应long double参数。(C99

               支持%LF,但是SUSv2不支持。)这是ll的同义词。

        j      后面跟一个“整数转换”对应一个intmax_t或者uintmax_t参数,或者跟一个

               n的转换对应一个指向intmax_t参数的指针。

        z      后面跟一个“整数转换”对应一个size_t或者ssize_t参数,或者跟一个n的

               转换对应一个指向size_t参数的指针。

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

    转换说明符

        转换说明符指定转换的类型,转换说明符及其含义如下:

        d, i     int类型参数转换为有符号十进制。如果指定了精度值,那么精度值表示

               结果中必须至少出现的数字的个数,如果转换的值的数字个数小于精度值,

               那么在左边补0。默认精度值为1。如果转换0时指定精度值为0,那么输

               出为空。

        o, u, x, X

               unsigned int类型参数转换为无符号八进制(o),无符号十进制(u),

               或者无符号十六进制(x和X)。字符abcdef用于x的转换,字符ABCDEF

               用于X的转换。如果指定了精度值,那么精度值表示结果中必须至少出现

               的数字的个数,如果转换的值的数字个数小于精度值,那么在左边补0。

               默认精度值为1。如果转换0时指定精度值为0,那么输出为空。

       e, E     double类型参数四舍五入并转换成[-]d.ddde±dd的格式,小数点前面有一

               个数字,小数点后面数字的个数等于精度值。如果没有指定精度值,那么

               默认为6。如果精度值为0,那么不会出现小数点。E的转换在指数中使用

               E(而不是e)。指数总是至少包含两个数字。如果值是0,那么指数为00。

       f, F     double类型参数四舍五入并转换成[-]ddd.ddd的格式,小数点后面数字的

               个数等于精度值。如果没有指定精度值,那么默认为6。如果精度值为0,

               那么不会出现小数点。如果出现了小数点,那么至少会有一个数字出现在

               小数点的前面。

               (SUSv2不支持F,并指明可以提供字符串来代表无穷大和NaN。SUSv3

               支持了F。C99在f转换中使用"[-]inf"或者"[-]infinity"代表无穷大,"nan"

               代表NaN,在F转换中使用"[-]INF"或者"[-]INFINITY"或者"NAN*"。)

       g, G     double类型参数以f或者e的方式转换(G则对应F或者E)。精度值表

               示有效数字的个数。如果没有指定精度值,那么默认为6。如果精度值为0,

               那么它被当成1。如果转换后的指数小于-4或大于等于精度值,则使用e

               的方式转换。从结果的小数部分删除尾部的0。只有小数点后面至少有一

               个数字时,小数点才会出现。

       a, A     (C99支持,SUSv2不支持,SUSv3支持)double类型参数转换成

               [-]0xh.hhhhp±的十六进制形式。A转换使用了前缀0X,字符ABCDEF和指

               数分隔符P。小数点之前会有一个十六进制的数字,小数点之后数字的个

               数等于精度值。如果以2为底能足够精确表示一个值,那么就用默认精度

               值,否则精度值就会很大并以此区分double类型的值。小数点前的数字对

               于非规范化数字是未指定的,对于规范化数字是非零但未指定的。

       c      如果没有指定l修饰符,那么int类型参数转换为unsigned char类型并写出

              对应的字符。如果指定l修饰符,通过调用wcrtomb()函数将wint_t(宽字

              符)类型参数转换为多字节序列,转换状态从初始状态开始,并写入生成的

              多字节字符串。

       s      如果没有指定l修饰符,那么const char *参数被解释成指向字符数组的指针

              (指向字符串),数组中的字符被写入,直至(但不包括)结束符('\0')。如

              果指定了精度值,那么写出的字符数不会超过精度值。如果指定了精度值,

              那么数组可以没有结束符。如果没有指定精度值或者指定的精度值大于数组

              的大小,那么这个数组必须要包含一个结束符。

       C      (C99和C11不支持,SUSv2、SUSv3、SUSv4支持。)lc的同义词。不要

               使用它。

       S      (C99和C11不支持,SUSv2、SUSv3、SUSv4支持。)ls的同义词。不要

               使用它。

       p      以十六进制方式打印void *指针参数(就好像使用%#x或者%#lx)。

       n      到目前为止写入的字符数存储到相应参数所指向的整数中,这个参数应该是

              一个int *,或者是一个变体,这个变体的大小能够匹配(可选)长度修饰符。

              不做转换参数的动作。如果转换规则中包含了标识符、字段宽度或者精度

              值,那么结果是未定义的。

       m      (Glibc的扩展。)打印strerror(errno)的输出,不需要参数。

       %      写出一个'%'。没有参数被转换。完整的转换规则是'%%'。

返回值

        成功时,这些函数返回打印的字符的数量(不包括用于结束输出字符串的结束符)。

        函数snprintf和vsnprintf写不超过size个字节(包括结束符(‘\0’))。如果输出

        因为该限制而被截断,那么返回值为空间足够时最终应该输出的字符数量(不包括

        结束符)。因此,返回值为size或者比size更大意味着输出被截断。(也可以阅

        读注意。)

        如果出错,则返回一个负数。

线程安全特征

接口

特征

printf(), fprintf(), sprintf(), snprintf(),vprintf(), vfprintf(), vsprintf(), vsnprintf()

线程安全

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标准化。

        SUSv2和C99对于函数snprintf的返回值相互矛盾,当参数size等于0时,SUSv2

        规定返回一个小于1的未定义值,在这种情况下C99允许str为NULL,C99(总是)

        返回空间足够时最终应该输出的字符数量。POSIX.1-2001及其后续规范以C99为

        准。

        glibc 2.1增加了长度修饰符hh、j、t和z,转换说明符a和A。

        glibc 2.2增加了符合C99语法的转换说明符F,标识符I。

注意

        一些程序轻率地使用如下代码追加文本到buf,

              sprintf(buf, "%s some further text", buf);

        然而,标准明确说明当调用sprintf,snprintf,vsprintf和vsnprintf时,如果源缓冲

        区和目的缓冲区有重叠,那么结果是未定义的。取决于gcc的版本和使用的编译器

        选项,调用上述类似代码将不会产生预期的结果。

        从glibc 2.1起,glibc以遵循C99的方式实现了函数snprintf和vsnprintf,使得函

        数的表现跟上面描述的一样。glibc 2.0.6及其之前的版本,当输出被截断时它们将

        输出-1。

BUGS

        因为函数sprintf和vsprintf接收任意长度的字符串,调用者必须小心不要溢出实际

        空间,这通常无法保证。请注意,生成的字符串长度取决于语言环境且难以预测。

        使用snprintf和vsnprintf代替(或asprintf 和 vasprintf)。

        形如printf(foo);的代码通常会导致bug,因为foo可能包含一个%字符,如果foo

        来自于一个不受信任的用户的输入,它可能包含%n,导致printf() 调用写入内存并

        产生一个安全漏洞。

示例

        打印Pi到小数点的后5位:

              #include <math.h>

              #include <stdio.h>

              fprintf(stdout, "pi = %.5f\n", 4 * atan(1.0));

        以"Sunday, July 3, 10:02"的形式打印日期和时间,weekday和month是指向字符串

        的指针:

              #include <stdio.h>

              fprintf(stdout, "%s, %s %d, %.2d:%.2d\n",

                      weekday, month, day, hour, min);

        许多国家使用日-月-年的顺序。因此一个国际化版本必须能按照参数format指定

        的顺序打印参数:

              #include <stdio.h>

              fprintf(stdout, format,

                      weekday, month, day, hour, min);

        format由语言环境决定,并且可能改变参数的排列。format为如下值:

              "%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 size = 0;

              char *p = NULL;

              va_list ap;

              /* 确定所需要的的大小 */

              va_start(ap, fmt);

              size = vsnprintf(p, size, fmt, ap);

              va_end(ap);

              if (size < 0)

                  return NULL;

              size++;             /* 保存'\0' */

              p = malloc(size);

              if (p == NULL)

                  return NULL;

              va_start(ap, fmt);

              size = vsnprintf(p, size, fmt, ap);

              if (size < 0) {

                  free(p);

                  return NULL;

              }

              va_end(ap);

              return p;

        }

        如果在glibc 2.0.6之前的版本中出现了截断,会被当成一个错误而不是被优雅地处

        理。

推荐阅读

        printf,asprintf,dprintf,puts,scanf,setlocale,wcrtomb,wprintf,locale

版本记录

        这个页面是Linux man-pages项目4.04版本的一部分。关于该项目的信息和bug

        报道可以在该网站找到:http://www.kernel.org/doc/man-pages/。

                                2015-08-08

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ta是一个搬运工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值