1、前言
我们经常涉及到数字与字符串之间的转换,例如将32位无符号整数的ip地址转换为点分十进制的ip地址字符串,或者反过来。从给定的字符串中提取相关内容,例如给定一个地址:http://www.bokeyuan.cn:2345,我们要从地址中提出协议,主机地址和端口号。之前对字符串和数字之间的关系不是很熟悉,工作中经常涉及到这个,如是好好总结一下。C语言提供了一些列的格式化输入输出函数,最基本的是面向控制台标准输出和输入的printf和scanf,其实还有面向字符串的sprint和sscanf,面向文件的流的fprintf和fscanf。今天着重总结一下sprintf和sscanf系列函数,这两个函数类似于scanf和printf ,不同点是从字符串*buffer用于输入输出。
2、sprintf函数
sprintf函数原型为 int sprintf(char *str, const char *format, ...)。作用是格式化字符串,具体功能如下所示:
(1)将数字变量转换为字符串。
(2)得到整型变量的16进制和8进制字符串。
(3)连接多个字符串。
举例如下所示:
1 char str[256] = { 0 };
2 int data = 1024;
3 //将data转换为字符串
4 sprintf(str,"%d",data);
5 //获取data的十六进制
6 sprintf(str,"0x%X",data);
7 //获取data的八进制
8 sprintf(str,"0%o",data);
9 const char *s1 = "Hello";
10 const char *s2 = "World";
11 //连接字符串s1和s2
12 sprintf(str,"%s %s",s1,s2);
3、sscanf函数
sscanf函数原型为int sscanf(const char *str, const char *format, ...)。将参数str的字符串根据参数format字符串来转换并格式化数据,转换后的结果存于对应的参数内。具体功能如下:
(1)根据格式从字符串中提取数据。如从字符串中取出整数、浮点数和字符串等。
(2)取指定长度的字符串
(3)取到指定字符为止的字符串
(4)取仅包含指定字符集的字符串
(5)取到指定字符集为止的字符串
sscanf可以支持格式字符%[]:
(1)-: 表示范围,如:%[1-9]表示只读取1-9这几个数字 %[a-z]表示只读取a-z小写字母,类似地 %[A-Z]只读取大写字母
(2)^: 表示不取,如:%[^1]表示读取除'1'以外的所有字符 %[^/]表示除/以外的所有字符
(3),: 范围可以用","相连接 如%[1-9,a-z]表示同时取1-9数字和a-z小写字母
(4)原则:从第一个在指定范围内的数字开始读取,到第一个不在范围内的数字结束%s 可以看成%[] 的一个特例 %[^ ](注意^后面有一个空格!)
解析网址的例子如下所示:
1 const char *s = "http://www.baidu.com:1234";
2 char protocol[32] = { 0 };
3 char host[128] = { 0 };
4 char port[8] = { 0 };
5 sscanf(s,"%[^:]://%[^:]:%[1-9]",protocol,host,port);
6
7 printf("protocol: %s\n",protocol);
8 printf("host: %s\n",host);
9 printf("port: %s\n",port);
10
4、snprintf函数
snprintf函数是sprintf函数的更加安全版本,考虑到字符串的字节数,防止了字符串溢出。函数形式为:int snprintf(char *restrict buf, size_t n, const char * restrict format, ...);。最多从源串中拷贝n-1个字符到目标串中,然后再在后面加一个0。所以如果目标串的大小为n 的话,将不会溢出。
5、测试程序
本次采用ip地址和整型之间的转换,mac地址转换作为测试程序,整个程序如下所示:
1 #include
2 #include
3
4 #define IP_STR_LEN 18
5 #define MAC_STR_LEN 18
6 #define MAC_BIT_LEN 6
7 #define LITTLE_ENDIAN 0
8 #define BIG_ENDIAN 1
9
10 typedef unsigned char uchar;
11 typedef unsigned int uint;
12
13 int big_little_endian()
14 {
15 int data = 0x1;
16 if (*((char*)&data) == 0x1)
17 return LITTLE_ENDIAN;
18 return BIG_ENDIAN;
19 }
20
21 uint ipstr2int(const char * ipstr)
22 {
23 assert(ipstr);
24 uint a,b,c,d;
25 uint ip = 0;
26 sscanf(ipstr,"%u.%u.%u.%u",&a,&b,&c,&d);
27 a = (a << 24) ;
28 b = (b << 16) ;
29 c = (c << 8) ;
30 d = (d << 0) ;
31 ip = a | b | c | d;
32 return ip;
33 }
34
35 char *int2ipstr(const uint ip, char *ipstr, const uint ip_str_len)
36 {
37 assert(ipstr);
38 if (big_little_endian() == LITTLE_ENDIAN)
39 sprintf(ipstr,"%u.%u.%u.%u",
40 (uchar)*((char*)(&ip)+3),
41 (uchar)*((char*)(&ip)+2),
42 (uchar)*((char*)(&ip)+1),
43 (uchar)*((char*)(&ip)+0));
44 else
45 sprintf(ipstr,"%u.%u.%u.%u",
46 (uchar)*((char*)(&ip)+0),
47 (uchar)*((char*)(&ip)+1),
48 (uchar)*((char*)(&ip)+2),
49 (uchar)*((char*)(&ip)+3));
50
51 return ipstr;
52 }
53
55 char *mac2str(const unsigned char *mac,char *mac_str,const uint mac_str_len)
56 {
57 assert(mac_str);
58 sprintf(mac_str,"%02X-%02X-%02X-%02X-%02X-%02X",
59 mac[0],mac[1],mac[2],
60 mac[3],mac[4],mac[5]);
61 }
62
63 int main()
64 {
65 char ip_str[IP_STR_LEN] = {0};
66 char mac_str[MAC_STR_LEN] = {0};
67 unsigned char mac[MAC_BIT_LEN] = {0XEF,0XAD,0XF4,0X4F,0XAA,0X0F};
68 const char *ipstr = "10.0.3.193";
69 unsigned int ip;
70 int2ipstr(167773121,ip_str,IP_STR_LEN);
71 mac2str(mac,mac_str,MAC_STR_LEN);
72 ip = ipstr2int(ipstr);
73 printf("%s\n",ip_str);
74 printf("%s\n",mac_str);
75 printf("ip:%u\n",ip);
76 return 0;
77 }
程序执行结果如下所示:
参考网址:
http://www.360doc.com/content/08/0813/22/45933_1539152.shtml
http://blog.csdn.net/wesweeky/article/details/6439777
http://msdn.microsoft.com/en-us/library/ce3zzk1k.aspx
http://technet.microsoft.com/zh-tw/library/wez13747
http://orgcent.com/ip-address-integer-convert/
sscanf("2006:03:18 - 2006:04:18", "%[0-9,:] - %[0-9,:]", sztime1, sztime2);
二 sprintf
- 负号时表示从后向前处理
0 表示空格填0
字符总宽度 小数点前面的数
精确度 小数点后面的数
1 把整数123 打印成一个字符串保存在s 中。
sprintf(s, "%d", 123); //产生"123"
2 sprintf(s, "%08X", 4567); //产生:"000011D7"
3 char a1[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
char a2[] = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
sprintf(s, "%.7s%.7s", a1, a2);//产生:"ABCDEFGHIJKLMN"
这可以类比打印浮点数的”%m.nf”,在”%m.ns”中,m 表示占用宽度(字符串长度不足时补空格,超出了则按照实际宽度打印),n 才表示从相应的字符串中最多取用的字符数。通常在打印字符串时m 没什么大用,还是点号后面的n 用的多。自然,也可以前后都只取部分字符:
sprintf(s, "%.6s%.5s", a1, a2);//产生:"ABCDEFHIJKL"
sprintf(s, "%.*s%.*s", 7, a1, 7, a2);
或者:
sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2);
4 实际上,前面介绍的打印字符、整数、浮点数等都可以动态指定那些常量值,比如:
sprintf(s, "%-*d", 4, 'A'); //产生"65 "
sprintf(s, "%#0*X", 8, 128); //产生"0X000080","#"产生0X
sprintf(s, "%*.*f", 10, 2, 3.1415926); //产生" 3.14"
5 sprintf(s, "%p", &i);
6 strlen 便已经知道了结果字符串的长度。如:
int len = sprintf(s, "%d", i);
对于正整数来说,len 便等于整数i 的10 进制位数。
sscanf:
sscanf() - 从一个字符串中读进与指定格式相符的数据.
函数原型:
Int sscanf( string str, string fmt, mixed var1, mixed var2 ... );
int scanf( const char *format [,argument]... );
说明:
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字节size。
5、type :这就很多了,就是%s,%d之类。
6、特别的:%*[width] [{h | l | I64 | L}]type 表示满足该条件的被过滤掉,不会向目标参数中写入值
支持集合操作:
%[a-z] 表示匹配a到z中任意字符,贪婪性(尽可能多的匹配)
%[aB'] 匹配a、B、'中一员,贪婪性
%[^a] 匹配非a的任意字符,贪婪性
例子:
1. 常见用法。
char buf[512] = {0};
sscanf("123456 ", "%s", buf);
printf("%s\n", buf);
结果为:123456
2. 取指定长度的字符串。如在下例中,取最大长度为4字节的字符串。
sscanf("123456 ", "%4s", buf);
printf("%s\n", buf);
结果为:1234
3. 取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。
sscanf("123456 abcdedf", "%[^ ]", buf);
printf("%s\n", buf);
结果为:123456
4. 取仅包含指定字符集的字符串。如在下例中,取仅包含1到9和小写字母的字符串。
sscanf("123456abcdedfBCDEF", "%[1-9a-z]", buf);
printf("%s\n", buf);
结果为:123456abcdedf
5. 取到指定字符集为止的字符串。如在下例中,取遇到大写字母为止的字符串。
sscanf("123456abcdedfBCDEF", "%[^A-Z]", buf);
printf("%s\n", buf);
结果为:123456abcdedf
6、给定一个字符串iios/12DDWDFF@122,获取 / 和 @ 之间的字符串,先将 "iios/"过滤掉,再将非'@'的一串内容送到buf中
sscanf("iios/12DDWDFF@122", "%*[^/]/%[^@]", buf);
printf("%s\n", buf);
结果为:12DDWDFF
7、给定一个字符串““hello, world”,仅保留world。(注意:“,”之后有一空格)
sscanf(“hello, world”, "%*s%s", buf);
printf("%s\n", buf);
结果为:world
%*s表示第一个匹配到的%s被过滤掉,即hello被过滤了
如果没有空格则结果为NULL。
Sprintf:
sprintf 是个变参函数,定义如下:
int sprintf( char *buffer, const char *format [, argument] ... );
除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:
格式化字符串上。
fprintf():
按格式输入到流,其原型是int fprintf(FILE *stream, const char *format[, argument, ...]);其用法和printf()相同,不过不是写到控制台,而是写到流罢了
例:fprintf(fp,"%2d%s",4,"Hahaha");
fscanf():
从流中按格式读取,其原型是int fscanf(FILE *stream, const char *format[, address, ...]);其用法和scanf()相同,不过不是从控制台读取,而是从流读取罢了。
例:fscanf(fp,"%d%d" ,&x,&y);
fscanf,sscanf,fprintf,sprintf还有scanf,printf
共同点是都是格式化输入/输出用的,它们的格式化方法/通配符都是一样的。
不同之处:
f打头的是文件I/O
s打头的是字符串I/O
什么都不带的, 在标准 键盘 屏幕(控制台) 出入输出.