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 <stdio.h>
2 #include <assert.h>
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/

scanf/fscanf 的%[]和%n使用方法

标准输入输出函数%[]和%n说明符的使用方法
   scanf fscanf,均从第一个非空格的可显示字符开始读起!

标准输入输出函数scanf具有相对较多的转换说明符,它常常作为入门级函数出现在各种教材中。但奇怪的是,[]和n这两种都为c89/c99所规定的标准说明符却鲜少在大多数教材中出现。虽然[]和n说明符的使用频率不及其它说明符,但两者在程序设计中的作用仍然不可小视,尤其是[]说明符。

众所周之,scanf以空白字符为定界符,但如果输入的字符串是以其它字符为定界符的,那怎么办?[]就是专门处理这个问题的转换说明符。[]转换说明符可以通过两种方式产生结果字符集,如果第一个[字符右边没有抑扬符(^),那么处于[]之间的字符就是结果字符集,不在其中的可输入字符都作为定界符;如果左边[符号紧靠一个抑扬符(^),那么意义相反,^和]之间的字符是定界符,其余可输入字符是结果字符集。

在使用[]说明符之前,得先明白两个概念:一是扫描列表。扫描列表(scanlist)指的是包含在[和]两个字符之间除紧靠左边[字符的抑扬符之外的字符,例如:

scanf("%[abcd]", ptr);

abcd组成扫描列表。二是扫描字符集(scanset)。扫描字符集指的是结果字符集,例如上面的例子,结果字符集就是abcd。如果输入一个字符串“cbadkjf”,那么ptr得到的字符串是cbad,kjf三个字符都属于定界符,输入到k字符时输入字符串被截断,kjf三个字符被留在stdin里面。如果带有抑扬符,例如:

scanf("%[^abcd]", ptr);

扫描列表仍然是abcd,但扫描字符集是除abcd外的可输入字符。如果输入字符串“jksferakjjdf”,ptr得到的字符串是“jksfer”。如果想限制输入字符串的字符数量,可以象s说明符那样,在[]前面使用位域,例如:

scanf("%10[^abcd]", ptr);

这样结果字符串最多只能包含10个字符(除'/0'字符外)。


[符号可以作为扫描列表中的一个成员,但]字符除紧贴最左边的[字符或抑扬符两种情况外,其余情况下都不会被看作扫描列表的成员。例如“%[]abcd]”或者“%[^]abcd]”,上述两种情况下]字符属于扫描列表的成员,但如果是“%[ab]cd]”,中间的]字符不会被看作扫描列表的成员,而且输入输出的结果会是乱七八糟的。

对于减号-,只有在紧贴[字符或抑扬字符以及作为扫描列表最后一个成员时,-字符才会被视为扫描列表的成员。c标准把其余情况规定为编译器相关的。大多数编译器把这种情况的减号定义为连字符,例如:

scanf("%[a-zA-Z]", ptr);

那么扫描列表由大小写各26个字母组成。少数编译器仍旧把这种情况下的减号视为扫描列表成员。
fscanf(fd,"%*[^/n]/n");//%*是虚读,没有存,只是让指针跳过了这个变量!

%n说明符输出有效字符数量,%n在scanf和printf中都可使用。与%n相对应的形参是一个int类型的指针,%n不影响scanf和printf的返回值。例如:

scanf("%d %d%n", &i, &j, &k);

如果输入434 6434,则k等于8,而scanf的返回值仍然为2。又如:

scanf("%c%n", &ch, &k);

输入“sbcdefdg”后,k等于1,而不是8,因为%c只取一个字符,%n输出的是有效字符数量

%n用在printf函数里,表示输出的字符数量,例如:

printf("i=%d, j=%d/n%n", i, j, &k);

在i=343、j=123的情况下,k=12,同时%n不影响printf的返回值,其返回值仍然为12,而不是14。

==============================================================

这个用法是在参H264 jm82考代码上看到的,用来从解码器参数配置文件中读取配置参数,代码如下:

// read the decoder configuration file
if((fd=fopen(config_filename,"r")) == NULL)
{
snprintf(errortext, ET_SIZE, "Error: Control file %s not found/n",config_filename);
error(errortext, 300);
}

fscanf(fd,"%s",inp->infile);                // H.26L compressed input bitsream
fscanf(fd,"%*[^/n]");

fscanf(fd,"%s",inp->outfile);               // YUV 4:2:2 input format
fscanf(fd,"%*[^/n]");

fscanf(fd,"%s",inp->reffile);               // reference file
fscanf(fd,"%*[^/n]");

对应的配置文件内容如下:

test.264                 ........H.26L coded bitstream
test_dec.yuv             ........Output file, YUV 4:2:0 format
test_rec.yuv             ........Ref sequence (for SNR)

通过这种方式

inp->infile = "test.264"

inp->outfile = "test_dec.yuv"

inp->reffile = "test_rec.yuv"

而相应的配置文件中的一些注释则不会被读入,这是相当简便的用法,比起通过严格约定注释符并进行一个字符一个字符来解析,这种方式简单了许多!值得借鉴!

===================================================================

scanf 
语法: 
  #include <stdio.h>
  int scanf( const char *format, ... );
 类似函数有
       int scanf(const char *format, ...);
       int fscanf(FILE *stream, const char *format, ...);//指定输入流
       int sscanf(const char *str, const char *format, ...);//指定缓存区
scanf()函数根据由format(格式)指定的格式从stdin(标准输入)读取,并保存数据到其它参数. 它和printf()有点类似. format(格式)字符串由控制字符,空白字符和非空白字符组成. 控制字符以一个%符号开始,如下:

控制字符 说明 
%c 一个单一的字符 
%d 一个十进制整数 
%i 一个整数 
%e, %f, %g 一个浮点数 
%o 一个八进制数 
%s 一个字符串 
%x 一个十六进制数 
%p 一个指针 
%n 一个等于读取字符数量的整数 
%u 一个无符号整数 
%[] 一个字符集 
%% 一个精度符号

1.scanf()读取匹配format(格式)字符串的输入.
当读取到一个控制字符, 它把值放置到下一个变量. 空白(tabs, 空格等等)会跳过. 非空白字符和输入匹配, 然后丢弃.
如果是一个在%符号和控制符间的数量, 那么只有指定数量的字符转换到变量中. 如果scanf()遇到一个字符集(用%[]控制字符表示),
那么在括号中的任意字符都会读取到变量中. scanf()的返回值是成功赋值的变量数量, 发生错误时返回EOF. 
2.scanf()函数的一般格式为:scanf("格式字符串",输入项首地址表)
3.scanf的格式控制的一般形式为:%[*][宽度][F|N][h|l]类型字符
[]中的控制字符为可选项 
4."*"表示该输入项读入后不赋予任何变量,即跳过该输入值。
5."宽度"表示输入读入字符的长度,对于整型表示截取相应宽度的数字赋给后面列表中的相应变量;对于字符型表示读入相应长度的字符后把第一个字符赋给相应的变量,其余的自动舍弃。例如scanf("%2d%3d",&a,
&b);如果输入为12345则将12赋给a,将45赋给b;scanf("%2c%3c",&a,
&b);如果输入为12345则将'1'赋给a,将'3'赋给b .
"%s" 整个输入作为一个串,并设置末尾的'/0'
"%ns",n为整数,读入的串最长不超过n,然后在末尾补'/0'
%nf 读入的浮点数最多有n位整数,位数多于n,会截断。
"%n[a-z]" 读入最多n个字符,如果遇到非a-z的字符,停止
"%[^=]" 读入任意多的字符,直到遇到"="停止
"%n[^=]" 读入"="号前的至多n 个字符
6.F 、N、h、l分别表示远指针、近指针、短整和长整型。
7.对于输入字符串还有一些比较有用的控制。
    例如经常需要读入一行字符串,而这串字符里面可能有空格、制表符等空白字符,如果直接用%s是不可以的,于是有些人就想到用gets(),当然这也是一种选择,但是懂C的人基本上都知道gets()是一个很危险的函数,而且很难控制,特别是与scanf()交替使用时前者的劣势更是一览无余,所以gets()一般是不推荐用的,其实用%[^/n]就可以很好的解决这个问题了,^表示"非",即读入其后面的字符就结束读入。这样想读入一行字符串直接用scanf("%[^/n]%*c",str);就可以了,
%*c的作用是读入/n,否则后面读入的将一直是/n。
所有对%s起作用的控制都可以用%[],比如%[0-9]表示只读入'0'到'9'之间的字符,%[a-zA-Z]表示只读入字母
'-'是范围连接符,当然也可以直接列出你需要读入的字符。
如果你只需要读"abc"里面的字符就可以用%[abc] (或者%[cab]、%[acb]、%[a-c]、%[c-a].....),
如果想读入某个范围之外的字符串就在前面加一个'^',如:%[^a-z]就表示读入小写字母之外的字符。
例如从键盘输入的"1235ab86"中读取1235、86给n,有如下方法:

  1. #include <stdio.h>
  2. bool skip(){
  3. scanf("%*[^0-9]");
  4. return true;
  5. }
  6. void main()
  7. {
  8. int n;
  9. while(skip() && scanf("%d", &n)!=EOF)
  10. printf("%d/n", n);
  11. }

输出为:

1235
86

C语言sprintf与sscanf函数的更多相关文章

  1. C语言sprintf和sscanf函数用法

    以前刚用C语言的时候,觉得字符串很难处理,后来用多了,发现其实并非如此,C语言也提供了许多函数给程序员使用.今天记录一下两个常用的两个字符串处理函数:sprintf和sscanf 1. sprintf ...

  2. C语言sprintf与sscanf函数[总结]

    sprintf函数 sprintf函数原型为 int sprintf(char *str, const char *format, ...).作用是格式化字符串,具体功能如下所示: (1)将数字变量转 ...

  3. c语言 sscanf()函数

    sscanf()函数用于从字符串中读取指定格式的数据,其原型如下:    int sscanf (char *str, char * format [, argument, ...]); [参数]参数 ...

  4. sscanf函数——强大的C语言库函数

    这个函数真的很强大,这篇随笔也将一直更新一些总结,直到涵盖大多数应用: eg1: 今天公司项目中从云端获取了一段字符串---"+CBC: 0,90,4090" 我想获取其中的数字0 ...

  5. sscanf函数和正则表达式

    看了几篇介绍sscanf函数,真是发现自己好多东西没理解透,详细介绍使用在sscanf中使用正则表达式. 第一篇: 此文所有的实验都是基于下面的程序: char str[10]; for (int i ...

  6. C语言:宽字符集操作函数(unicode编码)

    C语言:宽字符集操作函数(unicode编码) 字符分类: 宽字符函数 普通C函数描述 iswalnum() isalnum() 测试字符是否为数字或字母 iswalpha() isalpha() 测 ...

  7. C语言:宽字符集操作函数

    C语言:宽字符集操作函数 (unicode编码) 字符分类: 宽字符函数普通C函数描述 iswalnum() isalnum() 测试字符是否为数字或字母 iswalpha() isalpha() 测 ...

  8. Go语言的各种Print函数

    Go语言的各种Print函数 func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) func Pr ...

  9. linux c语言 fork() 和 exec 函数的简介和用法

    linux c语言 fork() 和 exec 函数的简介和用法   假如我们在编写1个c程序时想调用1个shell脚本或者执行1段 bash shell命令, 应该如何实现呢? 其实在<std ...

随机推荐

  1. SCSI contrller的几种类型的区别

    在VMware vSphere Web Client中, 可以为虚拟机添加一个新的SCSI controller, 选项中包含如下的类型, 那么他们有什么区别呢? 如何选择呢?   BusLogic ...

  2. 解决百度ueditor支持iframe框架页面的视频播放问题

    新下载的ueditor 增加了xss 安全过虑,把iframe过滤了,导致发表的文章包含的视频播放功能被限制了. 说明:新版本ueditor要修改 xss过滤白名单 修改配置文件ueditor.con ...

  3. GridControl 获取某分组的第一个孩子

    int iGroupRowHandle = this.gridControlView.FocusedRowHandle; ) { int iChildCount = this.gridControl. ...

  4. 【UI设计】扁平化设计之流行色值

    收集了一些颜色值 顏色表示方法: 以命名方式定义经常使用的顏色,如color="green".可是自由度较低,何况单词量...... 以RGB值表示.如#FF0000表示red(红 ...

  5. PHP的CLI综合

    tip1:传入参数 使用标准的输入和输出    PHP CLI会定义三个常量,以便让在命令行提示符下与解释器进行交互操作更加容易.这些常量见表格A.表格A 常量 说明STDIN 标准的输入设备STDO ...

  6. Appium Python 三:Desired capabilities

    简介 Desired capabilities 是一些键值对的集合.python里面就采用字典的方式. 客户端将这些键值对发给服务端,告诉服务端我们想要怎么测试.比如,我们可以把 platformNa ...

  7. ElasticSearch异常归纳(能力工场小马哥)

    异常1: can not run elasticsearch as root [WARN ][o.e.b.ElasticsearchUncaughtExceptionHandler] [node-2] ...

  8. margin 负边距应用

    margin-right:负值,在没有设置DOM元素宽度的前提下,DOM元素宽度变宽. <!DOCTYPE html> <html lang="zh-CN"> ...

  9. MariaDB删除重复记录

    不管是程序BUG,还是业务变更,重复数据这个老生常谈的问题,总是会出现.以下是我在MariaDB或是MySQL下处理的一些经验.在SQL Server中,使用窗口函数是很容易实现的.不过听说MySQL ...

  10. Unity 添加自定义菜单(插件),添加功能

    网上介绍如何写这种插件的文章很多...但是对于新手来说,最基本的,怎么运行这个插件,都不知道...网上的文章都懒得说这个...   幸好,看了半天官方网站别的资料,突然就发现办法了...   这个不是 ...