格式化I/O(常用的 sprintf 和 sscanf 总结)

1.格式化输出

#include 
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);
int dprintf(int fd, const char *restrict format, ...);
int sprintf(char *restrict buf, const char *restrict format, ...);
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);
  • printf 将格式化数据写到标准输出,fprintf 写至指定流,dprintf 写至指定文件描述符。
  • sprintf 将格式化字符写入数组 buf。sprintf 在该数组的尾端自动加入一个 null 字节,但该字符不包括在返回值中。sprintf 函数可能会造成由 buf 指向的缓冲区的溢出。调用者有责任确保该缓冲区足够大。缓冲区溢出会造成程序不稳定甚至安全隐患,为解决这种缓冲区溢出问题,引入 snprintf 函数。
  • snprintf 函数中,缓冲区长度是一个显式参数,超过缓冲区尾端写的所有字符都被丢弃。如果缓冲区足够大,snprintf 会返回写入缓冲区的字符数,该值不包括尾端的 null 字节。若 snprintf 函数返回小于缓冲区长度的正值,则没有截断输出;若返回负值,则说明出现编码错误。

格式说明控制其余参数如何编写,以及如何显示。每个参数按照转换说明编写,转换说明以百分号%开始。一个转换说明有4个可选部分:

%[flag][fldwidth][precision][lenmodifier]convtype

flag:

标志 说明
(撇号)将整数按千位分组字符
- 在字段内左对齐输出
+ 总是显示带符号转换的正负号
(空格) 如果第一个字符不是正负号,则在其前加入一个空格
# 指定另一种转换形式
0 添加前导0(而非空格)进行填充

fldwidth:说明最小字段宽度
precision:说明整型转换后最少输出数字位数、浮点数转换后小数点后最少位数、字符串转换后最大字节数。精度为一个点(.),其后跟随一个可选非负十进制数或星号(*)。
convtype:

转换类型 说明
d、i 有符号十进制
o 无符号八进制
u 无符号十进制
x、X 无符号十六进制
f、F 双精度浮点数
e、E 指数格式双精度浮点数
g、G 根据转换后的值解释为 f、F、e、E
a、A 十六进制指数格式双精度浮点数
c 字符
s 字符串
p 指向 void 的指针
n 到目前为止,此printf调用输出的字符数目将被写入到指针所指向的带符号整型中
% 一个 % 字符
C 宽字符
S 宽字符串

sprintf 是较常用的输出函数之一,用法如下:

a. 格式化数字字符串
sprintf最常见的应用之一莫过于把整数打印到字符串中,所以,spritnf在大多数场合可以替代itoa。如:

//把整数123打印成一个字符串保存在s中。
sprintf(s, "%d", 123);   // "123"
//可以指定宽度,不足的左边补空格:
sprintf(s, "%8d%8d", 123, 4567); // "     123    4567"
//当然也可以左对齐:
sprintf(s, "%-8d%8d", 123, 4567); // "123    4567"
//也可以按照16进制打印:
sprintf(s, "%8x", 4567); //小写16进制,宽度占8个位置,右对齐
sprintf(s, "%-8X", 4568); //大写16进制,宽度占8个位置,左对齐

b. 控制浮点数打印格式,默认保留小数点后6位

sprintf(s, "%f", 3.1415926);    // "3.141593"

但有时我们希望自己控制打印的宽度和小数位数,这时就应该使用:”%m.nf” 格式,其中m表示打印的宽度,n表示小数点后的位数。比如:

sprintf(s, "%10.3f", 3.1415626);   // "     3.142"
sprintf(s, "%-10.3f", 3.1415626);  // "3.142     "
sprintf(s, "%.3f", 3.1415626);     // "3.142"

对于如下代码:

int i = 123sprintf(s, "%.3f", i);

其结果是什么? “0.000”。。
原因是参数压栈时调用者并不知道跟 i 相对应的格式控制符是个”%f”。而函数执行时函数本身则并不知道当年被压入栈里的是个整数,于是保存整数 i 的那4个字节就被强行作为浮点数格式来解释了,导致出错了

sprintf(s, "%.3f", (float)i);     // 结果为 "123.000"

c.利用sprintf的返回值

例如,下面代码生成10个[0,100)的随机数,并输出到数组中,以逗号隔开:

#include 
#include 
#include 

int main() {
    srand(time(0));
    char s[64];
    int offset = 0;
    for (int i = 0; i < 10; i++) {
        offset += sprintf(s + offset, "%d,", rand() % 100);
    }
    s[offset - 1] = '\n';//将最后一个逗号换成换行符。
    printf(s);
    return 0;
}

2.格式化输入

#include 
int scanf(const char *restrict format, ...);
int fscanf(FILE *restrict fp, const char *restrict format, ...);
int sscanf(const char *restrict buf, const char *restrict format, ...);

scanf 族函数用于分析输入字符串,并将字符序列转换成指定类型的变量。在格式之后的各参数包含了变量的地址,用转换结果对这些变量赋值。

格式说明控制如何转换参数,以便对它们赋值。转换说明以百分号 % 开始,除转换说明和空白字符外,格式字符串中其他字符必须与输入匹配,若有一个字符不匹配,则停止后续处理,不再读输入的其他部分。

一个转换说明有 3 个如下的可选择的部分:

% [*][fldwidth][m][lenmodifier]convtype

其中,可选择的星号(*)用于抑制转换。按照转换说明的其余部分对输入进行转换,但转换结果并不存放在参数中。

fldwidth 说明最大宽度(即最大字符数),lenmodifier 说明要用转换结果赋值的参数大小。

convtype 字段类似于printf 族的转换类型字段,但两者之间还有些差别。一个差别是作为一种选项,输入中带符号的可赋予无符号类型。

在字段宽度和长度修饰符之间的可选项m 是赋值分配符,它可以用于%c、%s 以及 %[ 转换符,迫使内存缓冲区分配空间以接纳转换字符串。在这种情况下,相关参数必须是指针地址,分配的缓冲区地址必须复制给该指针。如果调用成功,该缓冲区不再使用时,由该调用者负责通过调用 free 函数来释放缓冲区。

转换类型 说明
d 有符号十进制,基数为10
i 有符号十进制,基数由输入格式决定
o 无符号八进制(输入可选地有符号)
u 无符号十进制,基数为10(输入可选地有符号)
x、X 无符号十六进制(输入可选地有符号)
a、A、e、E、f、F、g、G 浮点数
c 字符(若带长度修饰符l,为宽字符)
s 字符串(若带长度修饰符l,为宽字符串)
[ 匹配列出的字符序列,以 ] 终止
[^ 匹配除列出字符以外的所有字符,以 ] 终止
p 指向 void 的指针
n 到目前为止,此printf调用输出的字符数目将被写入到指针所指向的带符号整型中
% 一个 % 字符
C 宽字符
S 宽字符串

sscanf 与scanf 类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。其中的format可以是一个或多个:

{%[*] [width] [{h | l | I64 | L}]type | ' ' | '\t' | '\n' | 非%符号} 

注:

1.* 亦可用于格式中, (即 %*d 和 %*s) 加了星号 (*) 表示跳过此数据不读入. (也就是不把此数据读入参数中) 
2.{a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d。 
3.width表示读取宽度。 
4.{h | l | I64 | L}:参数的size,通常h表示单字节size,I表示2字节 size,L表示4字节size(double例外),l64表示8字节size5.type :%s,%d之类。 
6.特别的:%*[width] [{h | l | I64 | L}]type 表示满足该条件的被过滤掉,不会向目标参数中写入值 

  
支持集合操作:

%[a-z] 表示匹配a到z中任意字符,贪婪性(尽可能多的匹配) 
%[aB'] 匹配a、B、'中一员,贪婪性 
%[^a] 匹配非a的任意字符,贪婪性 

sscanf 用法:

a. 常见用法

char buf[512]; 
sscanf("123456 ", "%s", buf); 
printf("%s\n", buf);  // "123456"

b. 取指定长度的字符串。如在下例中,取最大长度为4字节的字符串。

sscanf("123456 ", "%4s", buf); 
printf("%s\n", buf);  // "1234"

c. 取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。

sscanf("123456 abcdedf", "%[^ ]", buf); 
printf("%s\n", buf);   // "123456"

d.取仅包含指定字符集的字符串。如在下例中,取仅包含1到9和小写字母的字符串。

sscanf("123456abcdedfBCDEF", "%[1-9a-z]", buf); 
printf("%s\n", buf);   // "123456abcdedf"


char s[] = "abcdef 1987:10:20";
char buf[512];
sscanf(s, "%[a-z0-9, ,:]", buf);   // "abcdef 1987:10:20"

e.取到指定字符集为止的字符串。如在下例中,取遇到大写字母为止的字符串。

sscanf("123456abcdedfBCDEF", "%[^A-Z]", buf); 
printf("%s\n", buf);   // "123456abcdedf"

f.给定一个字符串”iios/12DDWDFF@122”,获取 / 和 @ 之间的字符串,先将 “iios/”过滤掉,再将非’@’的一串内容送到buf中

sscanf("iios/12DDWDFF@122", "%*[^/]/%[^@]", buf); 
printf("%s\n", buf);   // "12DDWDFF" 

g.给定一个字符串““hello, world”,仅保留world。(注意:“,”之后有一空格)

sscanf(“hello, world”, "%*s%s", buf); //%*s表示第一个匹配到的%s被过滤掉,即hello被过滤了,如果没有空格则结果为NULL。
printf("%s\n", buf);   // "world"

h.提取字符串中的各个数字到各变量中

int a, b, c; 
sscanf("2016:04:21", "%d:%d:%d", a, b, c);  // a=2016, b=4, c=21
char sztime1[16] = "", sztime2[16] = ""; 
sscanf("2006:03:18 - 2006:04:18", "%s - %s", sztime1, sztime2); 

如果读取的字符串,不是以空格来分隔的话,就可以使用%[]。 %[]类似于一个正则表达式。[a-z]表示读取a-z的所有字符,[^a-z]表示读取除a-z以外的所有字符。 上述写法也可换作:  

sscanf("2006:03:18 - 2006:04:18", "%[0-9,:] - %[0-9,:]", sztime1, sztime2); 

sscanf 的功能很类似于正则表达式,但却没有正则表达式强大,所以如果对于比较复杂的字符串处理,建议使用正则表达式。

你可能感兴趣的:(C/C++)