关于sprintf与sscanf基本用法

说明

1.函数简介

sprintf & sscanf

函数原型:
①.sprintf:

// An highlighted block
int   _Cdecl sprintf  (char *buffer, const char *format, ...);

buffer:保存格式化信息
format:格式化字符串
… :可变参数,指明需要格式的数据

②.sscanf:

// An highlighted block
int  _Cdecl sscanf(const char *buffer, const char *format, ...);

buffer:格式化输入数据的来源
format:格式化字符串
… :可变参数,指明需要格式的数据

为了更好的理解参数内容我们引入printf作为一个对比项:
以上参数对比我们很容易发现我们常常使用的printf中也会具有“…”与“format”参数。“…”就是根据format格式控制因素决定“…”处变量的多少。
eg:在这里插入图片描述

A:部分就是format 一般叫做格式控制部分
B:部分叫做可变参数区由format区域格式控制符决定个数。

2. 梳理总结

**sprintf:**为格式化输出(方向从右到左)按格式合并
**sscanf:**为格式化输入(方向从左到右)按格式分离
注意上面的几点内容下面会用图文及例子说明原缘由:

sprintf基本使用:

/*******案例一:最基本使用********/
#include "stdio.h"
int main(void)
{
    
    
    char buff[10]={
    
    0};//声明字符串
    int a=13;//任意变量
    sprintf(buff,"%d",a);//将a变量按照格式写入buff中
    printf("buff:%s",buff);//打印写入后的buff
    return 0;
}

运行结果:buff:13

/**********案例二:以多格式控制符合并*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[20]={
    
    0};//扩大内容以保证容纳所有内容
    char strbuf[]="hello world";//待合并字符串
    int a=13,b=15;//十进制合并
    sprintf(buff,"%d%s%x",a,strbuf,b);//将上诉变量按格式合并进buff中
    printf("buff:%s",buff);//打印buff
    return 0;
}

运行结果:buff:13hello worldf
案例分析:a十进制为13 拼接字符串hello world再以16进制格式拼接十进制数15,15的16进制刚好是0xf

/***********案例三:按需求格式拼接字符串*******/
#include "stdio.h"
typedef struct 	//此处为了方便实验使用了一个结构体也可直接使用局部变量实验
{
    
    
        unsigned short int year;
        unsigned char mon;
        unsigned char day;
        unsigned char hour;
        unsigned char min;
        unsigned char sec;
}user_timer;
int main(void)
{
    
    
    user_timer  timestruct;//声明结构体变量
    timestruct.year=2006;//赋值
    timestruct.mon=12;
    timestruct.day=12;
    timestruct.hour=20;
    timestruct.min=20;
    timestruct.sec=59;
    char buff[30]={
    
    0};//扩展大小保证放下合并内容
    char strbuf[]="Today is";//添加字符串
	//下面按照你想要的格式拼接字符串
    sprintf(buff,"%s:%d-%d-%d %d:%d:%d",strbuf,timestruct.year,timestruct.mon,timestruct.day,timestruct.hour,timestruct.min,timestruct.sec);
    printf("buff:%s",buff);
    return 0;
}

运行结果:buff:Today is:2006-12-12 20:20:59
案例分析:本例子融合了案例二的其他格式同时又引入了自定义按需求规定格式控制符,这里就好像我们经常使用的printf一样 引号中你规定啥他打印啥,而这里的打印对象是一个char *的变量(即上例中的buff)。

现在是不是可以说明这句话“sprintf:**为格式化输出(方向从右到左)按格式合并”,它不正是按照格式从最右边参数合并到最左段的参数中。最终目的就是合并成一个字符串。

sscanf基本使用:

/***********案例一:字符串转换字符串*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[40]={
    
    0};
    char strbuf[]="Today is 2015-02-27 12:22:30";
    sscanf(strbuf,"%s",buff);//从左面参数按%s转换到buff中
    printf("buff:%s",buff);
    return 0;
}

结果:buff:Today
结果分析:为什么只复制了一个Today过来呢?其实sscanf与我们使用格式输入scanf是一样的遇见“空格”或者“\0”将会停止输入,切记!!!那么此时如何将空格过滤就要后面的2015-02-27呢?

/***********案例二:按格式拆分字符串*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[20]={
    
    0};//用于承接Todayis
    char buff2[20]={
    
    0};//用于承接2015-02-27
    char buff3[20]={
    
    0};//用于承接12:22:30
    char strbuf[]="Todayis 2015-02-27 12:22:30";
    sscanf(strbuf,"%s %s %s",buff,buff2,buff3);//注意"%s %s %s"间隔%s有空格
    printf("buff:%s\r\n",buff);
    printf("buff2:%s\r\n",buff2);
    printf("buff3:%s\r\n",buff3);
    return 0;
}

结果:
buff:Todayis
buff2:2015-02-27
buff3:12:22:30
结果分析:这里诠释了上例中解析中的问题项,这里使用前提是你知道“Todayis 2015-02-27 12:22:30”这个字符串三段使用“空格”为分隔,因此此例在sscanf格式控制中也使用了空格间隔将一个整串完成的差分成三包给对应的变量。

/***********案例三:上例变动引来的问题*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[20]={
    
    0};
    char buff2[20]={
    
    0};
    char buff3[20]={
    
    0};
    char strbuf[]="Todayis,2015-02-27,12:22:30";//修改源字符串
    sscanf(strbuf,"%s,%s,%s",buff,buff2,buff3);//修改格式控制
    printf("buff:%s\r\n",buff);
    printf("buff2:%s\r\n",buff2);
    printf("buff3:%s\r\n",buff3);
    return 0;
}

结果:
buff:Todayis,2015-02-27,12:22:30
buff2:
buff3:
结果分析:这个例子是上例的改动后的结果,我们会发现结果与上例的结论矛盾,其实我们仔细分析后"%s"打印会从字符串指定的首地址打印到"\0",例二正是因为sscanf与空格才分解了三包内容,那么在这里第一个%s只有遇见空格或者\0才会停止,因此第一个%s先打印了"Todayis,2015-02-27,12:22:30",而此时指针已经偏移到字符串的末尾,第二个%s或者第三个%s将什么也获取不到。

/***********案例四:引用前例加以说明*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[20]={
    
    0};
    char buff2[20]={
    
    0};
    char buff3[20]={
    
    0};
    char strbuf[]="Todayis, 2015-02-2712:22:30, 1234";//注意每个","后面的空格
    sscanf(strbuf,"%s, %s, %s",buff,buff2,buff3);//注意"%s,"后面的空格
    printf("buff:%s\r\n",buff);
    printf("buff2:%s\r\n",buff2);
    printf("buff3:%s\r\n",buff3);
    return 0;
}

运行结果:
buff:Todayis,
buff2:
buff3:
案例分析:解析如下图,如图当sscanf使用使用格式控制符中的第一个%s进行转换时,直到%s遇到\0或者空格才会结束,因此第一个%s将会打印到 “Todayis,” 此时sscanf格式控制符指向了第一个%s后面的“,”而此时在string中指向的是空格符,此时与空格符没有办法对应上,格式已经不正确因此后面所有的待转换变量都将错误。使用sscanf一定要注意对应格式。
在这里插入图片描述

/***********案例五:引用前例加以说明*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[20]={
    
    0};
    char buff2[20]={
    
    0};
    char buff3[20]={
    
    0};
    char strbuf[]="Todayis ,2015-02-2712:22:30 ,1234";//注意","前面的空格
    sscanf(strbuf,"%s ,%s ,%s",buff,buff2,buff3);//注意",%s"前面的空格
    printf("buff:%s\r\n",buff);
    printf("buff2:%s\r\n",buff2);
    printf("buff3:%s\r\n",buff3);
    return 0;
}

运行结果:
buff:Todayis
buff2:2015-02-2712:22:30
buff3:1234
案例分析:解析参考下图。案例五较比案例四改动不大但结果差异不小,首先第一个%s打印到源串第一个空格停止,及将Todayis复制到buff中。之后由于sscanf中同步了格式空格和“,”因此将偏移过这两个位置向下对应,从而第二个%s将对应“2015-02-2712:22:30 ”同理第三个%s将对应“1234”。
在这里插入图片描述

/***********案例六:验证格式输入一次指针会加一偏移*******/
 #include "stdio.h"
int main(void)
{
    
    
    char buff[20]={
    
    0};
    char buff2[20]={
    
    0};
    char buff3[20]={
    
    0};
    char strbuf[]="Todayis 2015-02-2712:22:30 1234";
    sscanf(strbuf,"%s%s%s",buff,buff2,buff3);//从案例一基础去除空格
    printf("buff:%s\r\n",buff);
    printf("buff2:%s\r\n",buff2);
    printf("buff3:%s\r\n",buff3);
    return 0;
}

运行结果:
buff:Todayis
buff2:2015-02-2712:22:30
buff3:1234
案例分析:从第一个%s打印到源串空格,因此buff中为“Todayis”,而此时指针加一,第二个格式控制符打印到第二个源串空格,因此buff2中为“2015-02-2712:22:30”以此类推。

/***********案例七:其他格式使用*******/
 #include "stdio.h"
int main(void)
{
    
    
    char buff[20]={
    
    0};
    int num=0;
    int cont=0;
    char strbuf[]="Thisis 10,22";
    sscanf(strbuf,"%s%d,%x",buff,&num,&cont);//这里与scanf一样别忘据取址符
    printf("buff:%s\r\n",buff);
    printf("buff2:%d\r\n",num);
    printf("buff3:%d\r\n",cont);//十进制打印
    return 0;
}

运行结果:
buff:Thisis
buff2:10
buff3:34
案例分析:首先在sscanf中第三个参数与scanf一样要取地址。这里使用将22直接以16进制形式到了cont中,因此打印时十六进制22的十进制为32。

现在是不是可以说明这句话“sscanf:**为格式化输入(方向从左到右)按格式拆分”,它不正是按照格式从最左边参数拆分到最右段的参数中。最终目的将一个字符串按格式拆分到对应的变量中。

3.加强补充

sprintf补充:

sprintf进一步说明:

/***********案例一:sprintf合并字符串单个提取*******/
#include <stdio.h>
    
int main(int argc, char** argv){
    
    
    char buf[5]={
    
    0};
    int a=10;
    int b=15;
    sprintf(buf,"%d%d",a,b);
    printf("%d\r\n",buf[0]);
    printf("%d\r\n",buf[1]);
    printf("%d\r\n",buf[2]);
    printf("%d\r\n",buf[3]);
    
  }

运行结果:
49
48
49
53
案例分析:通过sprintf将10与15拼接为一个字符串“1015”到buf中因此单个打印结果分别为对应字符的ascll码表中的十进制,例如字符1的ascll表对应的十进制数为49。

/***********案例二:sprintf增加特殊格式控制符*******/
#include "stdio.h"
typedef struct 
{
    
    
        unsigned short int year;
        unsigned char mon;
        unsigned char day;
        unsigned char hour;
        unsigned char min;
        unsigned char sec;
}user_timer;
int main(void)
{
    
    
    user_timer  timestruct;
    timestruct.year=2006;
    timestruct.mon=7;
    timestruct.day=2;
    timestruct.hour=2;
    timestruct.min=0;
    timestruct.sec=9;
    char buff[40]={
    
    0};
    char strbuf[]="Today is";
    double temp=29.4567;//加个浮点数温度体现sprintf可以通过%f进行精确定位
    sprintf(buff,"%s:%04d-%02d-%02d %02d:%02d:%02d temp:%5.2f",strbuf,timestruct.year,timestruct.mon,timestruct.day,timestruct.hour,timestruct.min,timestruct.sec,temp);
    printf("%s\r\n",buff);
    return 0;
}

运行结果:
Today is:2006-07-02 02:00:09 temp:29.46
案例分析:这里较好的体现了sprintf对字符串处理的优势,实现了年月日等的定长,例如2月不在是2,而是保持02,同时又可进行浮点数精度内容格式控制。

sprintf返回值说明:

sprintf返回的是写入的字符总数,但不包括结尾的字符串结束符,如果失败将返回一个负数。

/***********案例:引入前例测试返回值******/
#include "stdio.h"
typedef struct 	//此处为了方便实验使用了一个结构体也可直接使用局部变量实验
{
    
    
        unsigned short int year;
        unsigned char mon;
        unsigned char day;
        unsigned char hour;
        unsigned char min;
        unsigned char sec;
}user_timer;
int main(void)
{
    
    
    user_timer  timestruct;//声明结构体变量
    timestruct.year=2006;//赋值
    timestruct.mon=12;
    timestruct.day=12;
    timestruct.hour=20;
    timestruct.min=20;
    timestruct.sec=59;
    char buff[30]={
    
    0};//扩展大小保证放下合并内容
    char strbuf[]="Today is";//添加字符串
	//下面按照你想要的格式拼接字符串
    int ret=sprintf(buff,"%s:%d-%d-%d %d:%d:%d",strbuf,timestruct.year,timestruct.mon,timestruct.day,timestruct.hour,timestruct.min,timestruct.sec);
    printf("buff:%s\r\n",buff);
    printf("ret:%d--%d\r\n",strlen(buff),ret);
    return 0;
}

运行结果:
buff:Today is:2006-12-12 20:20:59
ret:28–28
案例分析:使用strlen结果与ret值相等,验证了sprintf返回值不包括字符串结束符的说法。

sscanf补充:

sscanf格式控制说明:

注意sscanf不可以进行浮点数精度控制!!!
注意sscanf不可以进行浮点数精度控制!!!
注意sscanf不可以进行浮点数精度控制!!!
且看下面两个例子

/***********案例一:sscanf不可进行浮点数精度控制*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[]="45.2755";
    float num=0;
    sscanf(buff,"%f",&num);
    printf("buff:%f\r\n",num);
    return 0;
}

运行结果:
buff:45.27550

/***********案例一:sscanf不可进行浮点数精度控制*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[]="45.2755";
    float num=0;
    sscanf(buff,"%5.2f",&num);
    printf("buff:%f\r\n",num);
    return 0;
}

运行结果:
buff:0.00000
说明:编译时会存在如下警告内容
warning: unknown conversion type character ‘.’ in format [-Wformat=]
sscanf(buff,“%5.2f”,&num);
^
warning: too many arguments for format [-Wformat-extra-args]
sscanf(buff,“%5.2f”,&num);
^~~~~~~
结果分析:在使用sscanf时无法识别格式控制符内容因此切记不要使用sscanf进行格式控制

sscanf返回值的说明:

sscanf返回值为被成功转换变量的个数,例如sscanf(buf,“%s%d%x”,buffer,&a,&b);当buffer及a和b成功获取到值时将返回3,当三者都没有被成功转换值将为0。

且看下面的示例:

/***********案例一:返回值测试1*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[20]={
    
    0};
    char buff2[20]={
    
    0};
    char buff3[20]={
    
    0};
    char strbuf[]="Todayis, 2015-02-2712:22:30, 1234";//注意每个","后面的空格
    int ret=sscanf(strbuf,"%s, %s, %s",buff,buff2,buff3);//注意"%s,"后面的空格
    printf("buff:%s\r\n",buff);
    printf("buff2:%s\r\n",buff2);
    printf("buff3:%s\r\n",buff3);
    printf("ret:%d\r\n",ret);
    return 0;
}

运行结果:
buff:Todayis,
buff2:
buff3:
ret:1
结果分析:引用之前例子,此处只转换成功了一个值,因此ret值为1

/***********案例二:字符转换测试******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[20]={
    
    0};
    char strbuf[]="A a";// 假设字符ascii码为A B
    int A,a;
    int ret=sscanf(strbuf,"%lc %lc",&A,&a); 
    printf("A:%d--%c\r\n",A,A);
    printf("a:%d--%c\r\n",a,a);
    printf("ret:%d\r\n",ret);
    return 0;
}

运行结果:
A:65–A
a:97–a
ret:2
结果分析:将A a进行字符格式赋值到A,与a变量中。返回值ret=2;成功2个

正则表达式:

正则表达式可以让sscanf很好的处理字符串的拆分,例如当你通过上位机发送一条命令串“set,1,2”;可以使用%*[^,]来过滤掉set 从而再经过一些列操作进行截取该指令有效数据。

%[^,]意思就是读取到非“,”的位置
说明:
1.%还是变量参数的格式控制符
2. [ ]代表你用的是正则表达式
3. ^代表非的意思
4. ,就是你认为读到的位置
例如:set,1,2 使用该格式控制内容将读取到set;

/***********案例二:%[^,]使用演示*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[]="set,1,2";
    char  strbuf[10]={
    
    0};
    sscanf(buff,"%[^,]",strbuf);
    printf("strbuf:%s\r\n",strbuf);
    return 0;
}

运行结果:strbuf:set

%* [1-9]%* [a-z]%[A-Z]舍弃读到的1-9内容及a-z内容保留A-Z内容
说明:
1.%号后面的*代表舍弃读到的内容
2.a-z表示遍历所有a-z小写内容(仅限连续内容)

/***********案例三:%*[1-9]%*[a-z]%[A-Z]使用演示*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[]="1111testSSCANF";
    char  strbuf[10]={
    
    0};
    char  buf1[10]={
    
    0};
    char  buf2[10]={
    
    0};
    sscanf(buff,"%*[1-9]%*[a-z]%[A-Z]",strbuf);
    printf("strbuf:%s\r\n",strbuf);
    return 0;
}

运行结果:strbuf:SSCANF

%[a-z] 表示匹配a到z中任意字符 注意此内容需要组合其他格式控制符使用否则返回空串。

/***********案例三:%[a-z]使用演示*******/
#include "stdio.h"
int main(void)
{
    
    
    char buff[]="1111testSSCANF";
    char  strbuf[10]={
    
    0};
    char  buf1[10]={
    
    0};
    char  buf2[10]={
    
    0};
    sscanf(buff,"%[a-z]",strbuf);
    printf("%s\r\n",strbuf);
    return 0;
}

运行结果

总结:

源串 格式控制符 结果
test, WORK(注意空格) %[^ ] test,
test, WORK(注意空格) %[a-z] test
123456testWORD %*[1-9]%[a-z] test
test:\\WORK\\config.txt(受c转移字符影响\必须为\\) %[^\\]\\%[A-Z]\\%s config.txt
URL:113.20.456.789:port:58000 %*[^ :]:%[^:] (^:中间无空格) 113.20.456.789
URL:113.20.456.789:port:58000 %[^ :]:%[^ :]:%*[^ :]:%s 58000
10:49:50:55 %[0-9,:] 10:49:50:55
hello\nword %*[^\n]%*c%[a-z] word
123test2345work %* [1-3]%[a-z]%* [^5]%s 串1:test 串2:work

猜你喜欢

转载自blog.csdn.net/CSDN_joker_/article/details/123366366