输入输出与格式化字符串(C/C++)

头文件<stdio.h>

C标准库头文件 <stdio.h> 中定义了三个变量类型、一些宏和各种函数来执行输入输出

三个库变量

下面是头文件 <stdio.h> 中定义的变量类型:

序号变量 & 描述
1 size_t
这是无符号整数类型,它是 sizeof 关键字的结果。
2 FILE
这是一个适合存储文件流信息的对象类型。
3 fpos_t
这是一个适合存储文件中任何位置的对象类型。

库宏

 下面是头文件 stdio.h 中定义的宏:

序号宏 & 描述
1 NULL
这个宏是一个空指针常量的值。
2 _IOFBF、_IOLBF 和 _IONBF
这些宏扩展了带有特定值的整型常量表达式,并适用于 setvbuf 函数的第三个参数。
3 BUFSIZ
这个宏是一个整数,该整数代表了 setbuf 函数使用的缓冲区大小。
4 EOFM
这个宏是一个表示已经到达文件结束的负整数。
5 FOPEN_MAX
这个宏是一个整数,该整数代表了系统可以同时打开的文件数量。
6 FILENAME_MAX
这个宏是一个整数,该整数代表了字符数组可以存储的文件名的最大长度。如果实现没有任何限制,则该值应为推荐的最大值。
7 L_tmpnam
这个宏是一个整数,该整数代表了字符数组可以存储的由 tmpnam 函数创建的临时文件名的最大长度。
8 SEEK_CUR、SEEK_END 和 SEEK_SET
这些宏是在These macros are used in the fseek 函数中使用,用于在一个文件中定位不同的位置。
9 TMP_MAX
这个宏是 tmpnam 函数可生成的独特文件名的最大数量。
10 stderr、stdin 和 stdout
这些宏是指向 FILE 类型的指针,分别对应于标准错误、标准输入和标准输出流。

库函数

下面是头文件 stdio.h 中定义的函数:

序号函数 & 描述
1 int fclose(FILE stream)
关闭流 stream。刷新所有的缓冲区。
2 void clearerr(FILE stream)
清除给定流 stream 的文件结束和错误标识符。
3 int feof(FILE stream)
测试给定流 stream 的文件结束标识符。
4 int ferror(FILE stream)
测试给定流 stream 的错误标识符。
5 int fflush(FILE stream)
刷新流 stream 的输出缓冲区。
6 int fgetpos(FILE stream, fpos_t pos)
获取流 stream 的当前文件位置,并把它写入到 pos。
7 FILE fopen(const char filename, const char mode)
使用给定的模式 mode 打开 filename 所指向的文件。
8 size_t fread(void ptr, size_t size, size_t nmemb, FILE stream)
从给定流 stream 读取数据到 ptr 所指向的数组中。
9 FILE freopen(const char filename, const char mode, FILE stream)
把一个新的文件名 filename 与给定的打开的流 stream 关联,同时关闭流中的旧文件。
10 int fseek(FILE stream, long int offset, int whence)
设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。
11 int fsetpos(FILE stream, const fpos_t pos)
设置给定流 stream 的文件位置为给定的位置。参数 pos 是由函数 fgetpos 给定的位置。
12 long int ftell(FILE stream)
返回给定流 stream 的当前文件位置。
13 size_t fwrite(const void ptr, size_t size, size_t nmemb, FILE stream)
把 ptr 所指向的数组中的数据写入到给定流 stream 中。
14 int remove(const char filename)
删除给定的文件名 filename,以便它不再被访问。
15 int rename(const char old_filename, const char new_filename)
把 old_filename 所指向的文件名改为 new_filename。
16 void rewind(FILE stream)
设置文件位置为给定流 stream 的文件的开头。
17 void setbuf(FILE stream, char buffer)
定义流 stream 应如何缓冲。
18 int setvbuf(FILE stream, char buffer, int mode, size_t size)
另一个定义流 stream 应如何缓冲的函数。
19 FILE tmpfile(void)
以二进制更新模式(wb+)创建临时文件。
20 char tmpnam(char str)
生成并返回一个有效的临时文件名,该文件名之前是不存在的。
21 int fprintf(FILE stream, const char format, ...)
发送格式化输出到流 stream 中。
22 int printf(const char format, ...)
发送格式化输出到标准输出 stdout。
23 int sprintf(char str, const char format, ...)
发送格式化输出到字符串。
24 int vfprintf(FILE stream, const char format, va_list arg)
使用参数列表发送格式化输出到流 stream 中。
25 int vprintf(const char format, va_list arg)
使用参数列表发送格式化输出到标准输出 stdout。
26 int vsprintf(char str, const char format, va_list arg)
使用参数列表发送格式化输出到字符串。
27 int fscanf(FILE stream, const char format, ...)
从流 stream 读取格式化输入。
28 int scanf(const char format, ...)
从标准输入 stdin 读取格式化输入。
29 int sscanf(const char str, const char format, ...)
从字符串读取格式化输入。
30 int fgetc(FILE stream)
从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
31 char fgets(char str, int n, FILE stream)
从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
32 int fputc(int char, FILE stream)
把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。
33 int fputs(const char str, FILE stream)
把字符串写入到指定的流 stream 中,但不包括空字符。
34 int getc(FILE stream)
从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
35 int getchar(void)
从标准输入 stdin 获取一个字符(一个无符号字符)。
36 char gets(char str)
从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
37 int putc(int char, FILE stream)
把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。
38 int putchar(int char)
把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中。
39 int puts(const char str)
把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中。
40 int ungetc(int char, FILE stream)
把字符 char(一个无符号字符)推入到指定的流 stream 中,以便它是下一个被读取到的字符。
41 void perror(const char str)
把一个描述性错误消息输出到标准错误 stderr。首先输出字符串 str,后跟一个冒号,然后是一个空格。

输入流

scanf

函数原型

int scanf(const char *format, ...);

描述

从标准输入 stdin 读取格式化输入。

参数

  • format -- 这是C字符串,包含了以下各项中的一个或多个:空格字符、非空格字符和format说明符(format说明符将在下面单独重点讲解)。
  • 附加参数 -- 根据不同的format字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了format参数中指定的每个%标签。参数的个数应与%标签的个数相同。

返回值

如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回EOF(-1)

sscanf

函数原型

int sscanf(const char *str, const char *format, ...);

描述

从字符串读取格式化输入。

参数

  • str -- 这是C字符串,是函数检索数据的源。
  • format -- 这是C字符串,包含了以下各项中的一个或多个:空格字符、非空格字符和format说明符。
  • 附加参数 -- 这个函数接受一系列的指针作为附加参数,每一个指针都指向一个对象,对象类型由format字符串中相应的 % 标签指定,参数与 % 标签的顺序相同。

针对检索数据的format字符串中的每个format说明符,应指定一个附加参数。如果您想要把sscanf操作的结果存储在一个普通的变量中,您应该在标识符前放置引用运算符(&),例如:

int n;
sscanf (str,"%d",&n);

返回值

如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回EOF(-1)

fscanf

函数原型

int fscanf(FILE *stream, const char *format, ...);

描述

从流stream读取格式化输入。

参数

  • stream -- 这是指向FILE对象的指针,该FILE对象标识了流。
  • format -- 这是C字符串,包含了以下各项中的一个或多个:空格字符、非空格字符和format说明符。
  • 附加参数 -- 根据不同的format字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了format参数中指定的每个%标签。参数的个数应与%标签的个数相同。

返回值

如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回EOF(-1)

format说明符

又称格式化字符串,输入流的format形式为%[*][width][modifiers]type],具体讲解如下:

参数 描述
* 这是一个可选的星号,表示从流stream中读取数据,但是忽略(跳过)它,即不将它存储在参数中
width 指定了当前读取操作中可读取的最大字符数
modifiers

为对应的附加参数所指向的数据指定一个不同于整型、无符号整型或浮点型的大小

h整型(针对d、i和n)或无符号短整型(针对o、u和x)

l整型(针对d、i和n)或无符号长整型(针对o、u和x),或双精度型针对e、f和g

L长双精度型(针对e、f和g)

type 一个字符,指定了要被读取的数据类型以及数据读取方式,具体参见下一个表格。

 scanf 类型说明符

类型合格的输入参数类型
c 单个字符:读取下一个字符。如果指定了一个不为1的宽度 width,函数会读取width个字符,并通过参数传递,把它们存储在数组中连续位置。在末尾不会追加空字符 char
d 十进制整数:数字前面的+或-号是可选的。 int
e,E,f,g,G 浮点数:包含了一个小数点、一个可选的前置符号+或-、一个可选的后置字符e或E,以及一个十进制数字。两个有效的实例 -732.103和7.12e4 float
o 八进制整数。 int
s 字符串。这将读取连续字符,直到遇到一个空格字符(空格字符可以是空白、换行和制表符)。 char
u 无符号的十进制整数。 unsigned int
x,X 十六进制整数。 int *

输出流

printf

函数原型

int printf(const char *format, ...);

描述

发送格式化输出到标准输出stdout。

参数

  • format -- 这是字符串,包含了要被写入到标准输出stdout的文本。它可以包含嵌入的format标签,format标签可被随后的附加参数中指定的值替换,并按需求进行格式化(格式化字符串format将在下面单独重点讲解)。
  • 附加参数 -- 根据不同的format字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了format参数中指定的每个 % 标签。参数的个数应与 % 标签的个数相同。

返回值

如果成功,则返回写入的字符总个数,否则返回一个负数

sprintf

函数原型

int sprintf(char *str, const char *format, ...);

描述

发送格式化输出到 str 所指向的字符串。

参数

  • str -- 这是指向一个字符数组的指针,该数组存储了C字符串。
  • format -- 这是字符串,包含了要被写入到字符串str的文本。它可以包含嵌入的format标签,format标签可被随后的附加参数中指定的值替换,并按需求进行格式化。
  • 附加参数 -- 根据不同的format字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了format参数中指定的每个%标签。参数的个数应与%标签的个数相同。

返回值

如果成功,则返回写入的字符总数不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数

fprintf

函数原型

int fprintf(FILE *stream, const char *format, ...);

描述

 发送格式化输出到流stream中。

参数

  • stream -- 这是指向FILE对象的指针,该FILE对象标识了流。
  • format -- 这是C字符串,包含了要被写入到流stream中的文本。它可以包含嵌入的format标签,format标签可被随后的附加参数中指定的值替换,并按需求进行格式化。
  • 附加参数 -- 根据不同的format字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了format参数中指定的每个%标签。参数的个数应与%标签的个数相同。

返回值

如果成功,则返回写入的字符总个数,否则返回一个负数

vprintf

函数原型

int vfprintf(FILE *stream, const char *format, va_list arg);

描述

使用参数列表发送格式化输出到流 stream 中。

参数

  • stream -- 这是指向FILE对象的指针,该FILE对象标识了流。
  • format -- 这是C字符串,包含了要被写入到流stream中的文本。它可以包含嵌入的format标签,format标签可被随后的附加参数中指定的值替换,并按需求进行格式化。
  • arg -- 一个表示可变参数列表的对象。这应被 中定义的va_start宏初始化。

返回值

如果成功,则返回写入的字符总数,否则返回一个负数。

vsprintf

函数原型

int vsprintf(char *str, const char *format, va_list arg);

描述

使用参数列表发送格式化输出到字符串。

参数

  • str -- 这是指向一个字符数组的指针,该数组存储了C字符串。
  • format -- 这是字符串,包含了要被写入到字符串str的文本。它可以包含嵌入的format标签,format标签可被随后的附加参数中指定的值替换,并按需求进行格式化。
  • arg -- 一个表示可变参数列表的对象。这应被 中定义的va_start宏初始化。

返回值

如果成功,则返回写入的字符总数,否则返回一个负数。

vfprintf 

函数原型

int vfprintf(FILE *stream, const char *format, va_list arg);

描述

使用参数列表发送格式化输出到流stream中。

参数

  • stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
  • format -- 这是 C 字符串,包含了要被写入到流 stream 中的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。
  • arg -- 一个表示可变参数列表的对象。这应被 中定义的va_start宏初始化。

返回值

如果成功,则返回写入的字符总数,否则返回一个负数。

printf中的格式化字符串

输出流以printf函数为基础,有一系列功能各异的输出函数(fprintf、sprintf、snprintf等),它们都在输出时使用了强大的格式化字符串。格式化字符串以一个%开始,以类型字段(d、f、x等)结束,完整格式如下:

%[参数][标志][宽度][.精度][长度]类型

注意:除了%类型之外均为可选字段。

参数

参数字段为POSIX扩展功能,非C99标准定义。使用 m$ 指定格式化字符串之后的第$m$个参数,从$14开始编号。假如使用了参数字段,则所有的参数必须都至少使用一次,即从 1$ 到 n$ 都必须至少出现一次(其中 n 为格式化字符串之后的参数个数),否则编译时将产生如下警告:

warning: missing $ operand number in format [-Wformat=]

借助参数字段,可以实现参数的多次使用、乱序使用等,而不需要重复书写参数。例如: printf("%2$d %2$#x; %1$d %1$#x",16,17); 将输出 17 0x11; 16 0x10 ,两个参数都使用了两次,且先使用第二个参数(同时也使用了下一节的 # 标志)。

标志

  •  - 左对齐(默认为右对齐)
  •  + :给正数附加符号前缀(默认为正数没有任何前缀)
  • ``(空格):给正数附加空格前缀(默认为正数没有任何前缀)
  •  0 :指定了宽度字段(见下文)且为右对齐时,前缀补0(默认补空格;当使用 - 标志指定了左对齐时, 0 标志失效)
  •  # :使用「备选格式」。对于g和G类型,不省略小数点部分最后的0;对于f、F、e、E、g、G类型,总是输出小数点;对于o、x、X类型,分别在非零数值前附加0、0x、0X 前缀。上一节的例子中就使用了 # 标志。

宽度

宽度字段指定输出字符的最小长度。长度不足的输出将使用填充字符补齐,填充字符及对齐方式使用上述的 0 标志和 - 标志确定;超长的输出不受影响(当然可使用下文的精度字段限定)。

当指定宽度字段时,可使用确定的整数值静态指定,也可使用 * 号由某个参数动态指定。例如 printf("%0*d", 5, 10); 将输出 00010 。注意:此例不能写成 %*0d 即颠倒了标志字段和宽度字段,否则将产生编译警告:

warning: unknown conversion type character ‘0in format [-Wformat=]
warning: too many arguments for format [-Wformat-extra-args]

精度

精度字段指定输出字符的最大长度。对浮点型数据来说,精度字段指定了小数点后的最长有效位数;对字符串来说,精度字段指定了输出的最大字符数。

与宽度字段相同,指定精度字段时,也可使用确定的整数值静态确定或 * 号动态确定。为了与宽度字段做区分,精度字段前必须加句点。例如 printf("%.*s", 3, "abcdef"); 将输出 abc 。假如没有句点,将被解析成宽度字段(最小长度)为$3$而精度字段(最大长度)未指定,因此将输出 abcdef 。

长度

  •  hh : 用于将 char 型参数转换成 int 型输出。
  •  h : 用于将 short  型参数转换成 int 型输出。
  •  l : 用于输出 long 型参数。对于浮点型无效果。
  •  ll : 用于输出 long long 型参数。
  •  L : 用于输出 long double 型参数。
  •  z : 用于输出 size_t 型参数。
  •  j : 用于输出 intmax_t 型参数。
  •  t : 用于输出 ptrdiff_t 型参数。

另有一些平台特定的非标准长度字符,如 I 、 I32 、 I64 、 q 等,可参考维基百科printf format string词条。

类型

  •  % : 原样输出一个 % 符号。此类型不接收任何其它字段,即只能使用 % 。
  •  d, i : 输出十进制 signed int 型数据。二者仅在使用 scanf 输入时有区别(使用 %i 将0x开头的数解析为十六进制,将0开头的数解析为八进制)。
  •  u : 输出十进制 unsigned int 型数据。
  •  f, F : 以定点数表示法输出 double 型数据。二者区别在于无限小数和NaN输出时是全小写的 inf 、 infinity 、 nan 还是全大写的 INF 、 INFINITY 、 NAN 。
  •  e, E : 以指数表示法输出 double 型数据。二者区别在于字符 e 的大小写。
  •  g, G : 根据指数自动选择定点数表示法或指数表示法。二者区别同样在于输出字符的大小写。以定点数表示法输出时与 f 和 F 的区别在于,可能省略小数部分最后的0或小数点(数据为整数时)。
  •  x, X : 输出十六进制 unsigned int 型数据。二者区别在于十六进制数的字符大小写。
  •  o : 输出八进制 unsigned int 型数据。
  •  s : 输出以 \0 结束的字符串。
  •  c : 输出一个 char 字符。
  •  p : 输出 void * ,输出格式信赖于具体实现。
  •  a, A : 输出十六进制 double 型数据,前缀0x或0X。
  •  n : 将当前的格式化字符串中已成功输出的字符数写入一个整型参数,字符数不包括 \n 、 \t 等转义字符。从输入输出的方向来说,此类型使用时更像是在 scanf 中接受输入,只不过输入不是来自用户、而是来自系统计数。注意:此计数仅对当前格式化字符串有效,重新开始一个格式化字符串时,计数将重置为0。例如 printf("%n", &num);  将把 num 赋值为0。

代码示例

可变宽度

可变宽度对于根据不同层次输出不同的缩进有奇效,例如:

<kernel/resource.c>
        seq_printf(m, "%*s%0*llx-%0*llx : %s\n",
                depth * 2, "",
                width, start,
                width, end,
                r->name ? r->name : "<BAD>");

其中的 %*s 表示输出 depth * 2 个空字符,字符个数是深度的两倍;两个 %0*llx 分别将 start 和 end 以十六进制 long long 型输出,最小宽度为 width ,宽度不足则以前导 0 补足。

十六进制输出

内核中大量使用了 # 标志配合 x 类型输出十六进制数,随便举一个例子:

<mm/slab.c>
    seq_printf(m, "%s+%#lx/%#lx", name, offset, size);

其中的两个 %#lx 即分别将 offset 和 size 以十六进制 long 型输出,并自动附加0x前缀。注意:此例中的 + 号不是 + 标志而是原样输出的字符,因为并不出现在 % 与类型字段之间。

另外,内核的 printk 所使用的格式化字符串有一些自定义的格式,可参考内核文档 How to get printk format specifiers right

(整理自网络)

参考资料:

https://wiki.jikexueyuan.com/project/c/c-standard-library-stdio-h.html

https://blog.lancitou.net/format-string-in-printf/

posted @ 2020-12-30 12:56  箐茗  阅读(1887)  评论(0编辑  收藏  举报