免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2394 | 回复: 0
打印 上一主题 下一主题

[Linux] Linux C下使用正则库 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-09-02 19:15 |只看该作者 |倒序浏览
本帖最后由 冷寒生 于 2012-09-02 19:15 编辑

说起正则表达式(Regular Expression),也许有的朋友天天都在使用,比如grep、vim、sed、awk,只是可能对这个名词不大熟悉。正则表达式一般简写为 regex或者regexp,甚至是RE。关于正则表达式的介绍,有很多的文章,用搜索引擎查找就可以找到很不错的使用说明。但是在C/C++语言中如何 去使用,相应的介绍比较缺乏。大多数C标准库自带regex,可以通过/usr/include/regex.h去看,或者man regex看使用说明。perl,php等语言更是提供了功能强大的正则表达式,最著名的C语言正则表达式库为PCRE(Perl Compatible Regular Expression)。本文主要对regex和pcre的使用做一点入门介绍。

1、regex
regex的使用非常简单,只要看一下示例代码1就能明白(示例代码是从“GNU C 规则表达式入门”这篇文章里摘取出来的,是否为原始出处就不得而知了)。

CODE:
#include <stdio.h>
#include <string.h>
#include <regex.h>

#define SUBSLEN 10              /* 匹配子串的数量 */
#define EBUFLEN 128             /* 错误消息buffer长度 */
#define BUFLEN 1024             /* 匹配到的字符串buffer长度 */

int main()
{
        size_t          len;
        regex_t         re;             /* 存储编译好的正则表达式,正则表达式在使用之前要经过编译 */
        regmatch_t      subs [SUBSLEN];     /* 存储匹配到的字符串位置 */
        char            matched   [BUFLEN];     /* 存储匹配到的字符串 */
        char            errbuf    [EBUFLEN];    /* 存储错误消息 */
        int             err, i;

        char            src       [] = "111 <title>Hello World</title> 222";    /* 源字符串 */
        char            pattern   [] = "<title>(.*)</title>";    /* pattern字符串 */

        printf("String : %s/n", src);
        printf("Pattern: /"%s/"/n", pattern);

        /* 编译正则表达式 */
        err = regcomp(&re, pattern, REG_EXTENDED);

        if (err) {
                len = regerror(err, &re, errbuf, sizeof(errbuf));
                printf("error: regcomp: %s/n", errbuf);
                return 1;
        }
        printf("Total has subexpression: %d/n", re.re_nsub);
        /* 执行模式匹配 */
        err = regexec(&re, src, (size_t) SUBSLEN, subs, 0);

        if (err == REG_NOMATCH) { /* 没有匹配成功 */
                printf("Sorry, no match .../n");
                regfree(&re);
                return 0;
        } else if (err) {  /* 其它错误 */
                len = regerror(err, &re, errbuf, sizeof(errbuf));
                printf("error: regexec: %s/n", errbuf);
                return 1;
        }

        /* 如果不是REG_NOMATCH并且没有其它错误,则模式匹配上 */
        printf("/nOK, has matched .../n/n");
        for (i = 0; i <= re.re_nsub; i++) {
                len = subs.rm_eo - subs.rm_so;
                if (i == 0) {
                        printf ("begin: %d, len = %d  ", subs.rm_so, len); /* 注释1 */
                } else {
                        printf("subexpression %d begin: %d, len = %d  ", i, subs.rm_so, len);
                }
                memcpy (matched, src + subs.rm_so, len);
                matched[len] = '/0';
                printf("match: %s/n", matched);
        }

        regfree(&re);   /* 用完了别忘了释放 */
        return (0);
}执行结果是

CODE:
String : 111 <title>Hello World</title> 222
Pattern: "<title>(.*)</title>"
Total has subexpression: 1

OK, has matched ...

begin: %, len = 4  match: <title>Hello World</title>
subexpression 1 begin: 11, len = 11  match: Hello World从 示例程序可以看出,使用之前先用regcomp()编译一下,然后调用regexec()进行实际匹配。如果只是看有没有匹配成功,掌握这2个函数的用法 即可。有时候我们想要取得匹配后的子表达式,比如示例中想获得title是什么,需要用小括号 "( )"把子表达式括起来"<title>(.*)</title>",表达式引擎会将小括号 "( )" 包含的表达式所匹配到的字符串记录下来。在获取匹配结果的时候,小括号包含的表达式所匹配到的字符串可以单独获取,示例程序就是我用来获取http网页的 主题(title)的方式。  

regmatch_t subs[SUBSLEN]是用来存放匹配位置的,subs[0]里存放这个匹配的字符串位置,subs[1]里存放第一个子表达式的匹配位置,也就是例 子中的title,通过结构里的rm_so和rm_eo可以取到,这一点很多人不太注意,应该强调一下。

注释1:开始调试代码的时候是在 FreeBSD 6.2上进行的,print出来的len总是0,但print出来的字符串又没错,很是迷惑,把它放到Linux上则完全正常,后来仔细检查才发现 rm_so在Linux上是32位,在FreeBSD上是64位,用%d的话实际取的是rm_so的高32位,而不是实际的len,把print rm_so的地方改为%llu就可以了。

regex虽然简单易用,但对正则表达式的支持不够强大,中文处理也有问题,于是引出了下面要说的PCRE。

参考文章:

c语言中的正则表达式
http://www.zzt123.com/html/2012/0812/1344754609.html

linux c与正则表达式
http://blog.sina.com.cn/s/blog_62b8329101010dpu.html

2、PCRE  (http://www.pcre.org
PCRE的名字就说明了是Perl Compatible,熟悉Perl、PHP的人使用起来完全没有问题。PCRE有非常丰富的使用说明和示例代码(看看pcredemo.c就能明白基本的用法),下面的程序只是把上面regex改为pcre。

CODE:
/* Compile thuswise:   
*   gcc -Wall pcre1.c -I/usr/local/include -L/usr/local/lib -R/usr/local/lib -lpcre
*      
*/     

#include <stdio.h>
#include <string.h>
#include <pcre.h>
               
#define OVECCOUNT 30    /* should be a multiple of 3 */
#define EBUFLEN 128            
#define BUFLEN 1024           
        
int main()
{               
        pcre            *re;
        const char      *error;
        int             erroffset;
        int             ovector[OVECCOUNT];
        int             rc, i;
        
        char            src    [] = "111 <title>Hello World</title> 222";
        char            pattern   [] = "<title>(.*)</title>";
               
        printf("String : %s/n", src);
        printf("Pattern: /"%s/"/n", pattern);
        

        re = pcre_compile(pattern, 0, &error, &erroffset, NULL);
        if (re == NULL) {
                printf("PCRE compilation failed at offset %d: %s/n", erroffset, error);
                return 1;
        }

        rc = pcre_exec(re, NULL, src, strlen(src), 0, 0, ovector, OVECCOUNT);
        if (rc < 0) {
                if (rc == PCRE_ERROR_NOMATCH) printf("Sorry, no match .../n");
                else    printf("Matching error %d/n", rc);
                free(re);
                return 1;
        }

        printf("/nOK, has matched .../n/n");

        for (i = 0; i < rc; i++) {
                char *substring_start = src + ovector[2*i];
                int substring_length = ovector[2*i+1] - ovector[2*i];
                printf("%2d: %.*s/n", i, substring_length, substring_start);
        }

        free(re);
        return 0;
}执行结果是:

CODE:
String : 111 <title>Hello World</title> 222
Pattern: "<title>(.*)</title>"

OK, has matched ...

0: <title>Hello World</title>
1: Hello World 比较这2个例子可以看出,在regex用的是regcomp()、regexec(),pcre则使用pcre_compile()、pcre_exec(),用法几乎完全一致。

pcre_compile()有很多选项,详细说明参见http://www.pcre.org/pcre.txt。如果是多行文本,可以设置PCRE_DOTALL的选项pcre_complie(re, PCRE_DOTALL,....),表示'.'也匹配回车换行"/r/n"。

3、pcre++
pcre++(http://www.daemon.de/PCRE)对pcre做了c++封装,使用起来更加方便。

CODE:
/*
* g++ pcre2.cpp -I/usr/local/include -L/usr/local/lib -R/usr/local/lib -lpcre++ -lpcre
*/
#include <string>
#include <iostream>
#include <pcre++.h>

using namespace std;
using namespace pcrepp;

int main()
{
        string src("111 <title>Hello World</title> 222");
        string pattern("<title>(.*)</title>");

        cout << "String : " << src << endl;
        cout << "Pattern : " << pattern << endl;

        Pcre reg(pattern, PCRE_DOTALL);
        if (reg.search(src) == true) { //
                cout << "/nOK, has matched .../n/n";
                for(int pos = 0; pos < reg.matches(); pos++) {
                        cout << pos << ": " << reg[pos] << endl;
                }
        } else {
                cout << "Sorry, no match .../n";
                return 1;
        }

        return 0;
}执行结果是:

CODE:
String : 111 <title>Hello World</title> 222
Pattern : <title>(.*)</title>

OK, has matched ...

0: Hello World4、oniguruma
还有一个正则表达式的库oniguruma(http://www.geocities.jp/kosako3/oniguruma/),对于东亚文字支持比较好,开始是用在ruby上,也可用于C++,是日本的开发人员编写的。大多数人都不会用到,也就不做介绍了。如果有疑问可以通过email来讨论它的用法。

5、Regular Expression的内部实现
关 于Regular Expression的实现,用到了不少自动机理论(Automata Theory)的知识,有兴趣的可以找这方面的资料来看,这本书“ Introduction to Automata Theory, Languages, and Computation”写的很好,编译原理的书也有这方面的内容。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP