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

头文件

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

三个库变量

下面是头文件中定义的变量类型:

**序号** **变量 & 描述**
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);将输出170x11;160x10,两个参数都使用了两次,且先使用第二个参数(同时也使用了下一节的#标志)。

标志

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

宽度

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

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

warning: unknown conversion type character ‘0’ in 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: 用于输出longlong型参数。
  • L: 用于输出longdouble型参数。
  • z: 用于输出size_t型参数。
  • j: 用于输出intmax_t型参数。
  • t: 用于输出ptrdiff_t型参数。

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

类型

  • %: 原样输出一个%符号。此类型不接收任何其它字段,即只能使用%
  • d, i: 输出十进制signedint型数据。二者仅在使用scanf输入时有区别(使用%i将0x开头的数解析为十六进制,将0开头的数解析为八进制)。
  • u: 输出十进制unsignedint型数据。
  • f, F: 以定点数表示法输出double型数据。二者区别在于无限小数和NaN输出时是全小写的infinfinitynan还是全大写的INFINFINITYNAN
  • e, E: 以指数表示法输出double型数据。二者区别在于字符e的大小写。
  • g, G: 根据指数自动选择定点数表示法或指数表示法。二者区别同样在于输出字符的大小写。以定点数表示法输出时与fF的区别在于,可能省略小数部分最后的0或小数点(数据为整数时)。
  • x, X: 输出十六进制unsignedint型数据。二者区别在于十六进制数的字符大小写。
  • o: 输出八进制unsignedint型数据。
  • 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分别将startend以十六进制longlong型输出,最小宽度为width,宽度不足则以前导0补足。

十六进制输出

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

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

其中的两个%#lx即分别将offsetsize以十六进制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/
原文链接: https://www.cnblogs.com/MinPage/p/14203734.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/206752

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月12日 下午10:40
下一篇 2023年2月12日 下午10:40

相关推荐