`
kenby
  • 浏览: 717254 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

sscanf函数和正则表达式

阅读更多

 

 

此文所有的实验都是基于下面的程序:

 

char str[10];
for (int i = 0; i < 10; i++) str[i] = '!';
 

 

执行完后str的值为

str = "!!!!!!!!!!"

我们把str的每个字符都初始化为惊叹号,当str的值发生变化时,使用printf打印str的值,对比先前的惊叹号,这样就可以方便的观察str发生了怎样的变化。下面我们做几个小实验,看看使用sscanf和正则表达式格式化输入后,str有什么变化。

实验1:

 

sscanf("123456","%s",str);    // str的值为 "123456\0!!!"
 

 

这个实验很简单,把源字符串"123456"拷贝到str的前6个字符,并且把str的第7个字符设为null字符,也就是\0

实验2:

 

sscanf("123456","%3s",str);  // str的值为 "123\0!!!!!!"
 

 

看到没有,正则表达式的百分号后面多了一个3,这告诉sscanf只拷贝3个字符给str,然后把第4个字符设为null字符。

实验3:

sscanf("aaaAAA","%[a-z]",str); ---------str的值为 "aaa\0!!!!!!"

从这个实验开始我们会使用正则表达式,括号里面的a-z就是一个正则表达式,它可以表示从a到z的任意字符,

在 继续讨论之前,我们先来看看百分号表示什么意思,%表示选择,一个%只能对应一个变量,%后面的是条件,比如实验1的"%s",s是一个条件,表示任意字符,"%s"的意思是:只 要输入的东西是一个字符,就把它拷贝给str。实验2的"%3s"又多了一个条件:只拷贝3个字符。实验3的“%[a-z]”的条件稍微严格一些,输入的 东西不但是字符,还得是一个小写字母的字符,所以实验3只拷贝了小写字母"aaa"给str,别忘了加上null字符。

实验4:

 

sscanf("AAAaaaBBB","%[^a-z]",str);    // str的值为 "AAA\0!!!!!!"
 

 

对 于所有字符,只要不是小写字母,都满足"^a-z"正则表达式,符号^表示逻辑非。前3个字符都不是小写字符,所以将其拷贝给str,但最后3个字符也不 是小写字母,为什么不拷贝给str呢?这是因为当碰到不满足条件的字符后,sscanf就会停止执行,不再扫描之后的字符。

实验5:

 

sscanf("AAAaaaBBB","%[A-Z]%[a-z]",str);     // 段错误
 

 

这 个实验的本意是:先把大写字母拷贝给str,然后把小写字母拷贝给str,但很不幸,程序运行的时候会发生段错误,因为当sscanf扫描到字符a时,违 反了条件"%[A-Z]",sscanf就停止执行,不再扫描之后的字符,所以第二个条件也就没有任何意义,这个实验说明:一个%号只能对应一个变量,%号的数量不能超过变量的个数

实验6:

 

sscanf("AAAaaaBBB","%*[A-Z]%[a-z]",str);   // str的值为 "aaa\0!!!!!!"
 

 

这 个实验出现了一个新的符号:%*,与%相反,%*表示过滤满足条件的字符,在这个实验中,%*[A-Z]过滤了所有大写字母,然后再使用%[a-z]把之 后的小写字母拷贝给str。如果只有%*,没有%的话,sscanf不会拷贝任何字符到str,这时sscanf的作用仅仅是过滤字符串。

实验7:

 

sscanf("AAAaaaBBB","%[a-z]",str);   // str的值为 "!!!!!!!!!!"
 

做完前面几个实验后,我们都知道sscanf拷贝完成后,还会在str的后面加上一个null字符,但如果没有一个字符满足条件,sscanf不会在str的后面加null字符,str的值依然是10个惊叹号。这个实验也说明了,如果不使用%*过滤掉前面不需要的字符,你永远别想取得中间的字符。

实验8:

 

sscanf("AAAaaaBC=","%*[A-Z]%*[a-z]%[^a-z=]",str);   // str的值为 "BC\0!!!!!!!"
 

这是一个综合实验,但这个实验的目的不是帮我们复习前面所学的知识,而是展示两个值得注意的地方:

注意1:%只能使用一次,但%*可以使用多次,比如在这个实验里面,先用%*[A-Z]过滤大写字母,然后用%*[a-z]过滤小写字母。

注意2:^后面可以带多个条件,且这些条件都受^的作用,比如^a-z=表示^a-z且^=(既不是小写字母,也不是等于号)。

实验9:

 

int k;
sscanf("AAA123BBB456", "%*[^0-9]%i", &k);  // k的值为123
 

首先,%*[^0-9]过滤前面非数字的字符,然后用%i把数字字符转换成int型的整数,拷贝到变量k,注意参数必须使用k的地址。

试验10:

 

char username[100];
char password[100];
sscanf("kenby =         yejinhui", "%[^=\t ]%*[{=|\t| }]%s", 
                   username, password); 
// username的值: "kenby",password的值: "yejinhui"
 

 

%[^=\t ]选择非=,非tab,非空格的字符,赋给username

%*[{=|\t| }] 过滤掉=或者tab或者空格

%s 选择剩下的字符赋给password

 

最后来看一个例子,从url中提取出主机名host和端口port,测试数据如下:

 

http://kenby.iteye.com/index.html 

http://kenby.iteye.com:80/index.html

kenby.iteye.com/index.html

kenby.iteye.com:80/index.html

 

对于上面4个url,提出出来的host为kenby.iteye.com,端口port = 80

如果有协议头,去掉,如果没有端口,默认为80

 

static int parse_url(const char *url, char *host, int *port) {
    int len;

    if (sscanf(url, "%*[htps]://%1024[^:]:%d%n", host, port, &len) == 2 ||
            sscanf(url, "%1024[^:]:%d%n", host, port, &len) == 2) {
    } else if (sscanf(url, "%*[htps]://%1024[^/]%n", host, &len) == 1) {
        *port = 80;
    } else {
        sscanf(url, "%1024[^/]%n", host, &len);
        *port = 80;
    }
    printf("%d\n", len);
    return len;
}

 

 

sscanf(url, "%*[htps]://%1024[^:]:%d%n", host, port, &len)

 

%*[htps]://     过滤掉h,t, p, s的字符, 以及://,

%1024[^:]      选择解下来的字符做为host,直到遇到:,1024表示最多选择1024个字符,超过了

                        1024个字符就停止匹配,后面的port也不会去匹配了

:                      过滤:

%d                  选择接下来的数字做为port

%n                  Nothing is expected; instead, the number of characters consumed thus far

                       from the input  is  stored  through the  next  pointer, which must be a pointer to int.

                       表示把此次扫描的字符个数存储到len中,有时可以根据len来判断字符串是否合法

关于sscanf的返回值: 表示提取了多少个变量

 

sscanf(url, "%1024[^:]:%d%n", host, port, &len)

sscanf(url, "%*[htps]://%1024[^/]%n", host, &len)

sscanf(url, "%1024[^/]%n", host, &len);

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics