在C/C++语言中使用正则表达式
本文所有的内容都来自网络,加上一些个人的理解,不保证正确性,欢迎批评指正
说起(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[i].rm_eo - subs[i].rm_so;
if (i == 0) {
printf ("begin: %d, len = %d ", subs[i].rm_so, len); /* 注释1 */
} else {
printf("subexpression %d begin: %d, len = %d ", i, subs[i].rm_so, len);
}
memcpy (matched, src + subs[i].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。
2、pcre ()
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()有很多选项,详细说明参见。如果是多行文本,可以设置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 world
4、oniguruma
还有一个的库oniguruma(),对于东亚文字支持比较好,开始是用在ruby上,也可用于c++,是日本的开发人员编写的。大多数人都不会用到,也就不做介绍了。如果有疑问可以通过email来讨论它的用法。
5、regular expression的内部实现
关于regular expression的实现,用到了不少自动机理论(automata theory)的知识,有兴趣的可以找这方面的资料来看,这本书“
introduction to automata theory, languages, and computation”写的很好,编译原理的书也有这方面的内容。
上一篇: 如何对网店店铺进行优化?有哪些技巧?
下一篇: 明知道刷单犹如饮鸠止渴,商家又该怎么做?
推荐阅读
-
DEV C++在win7系统中安装以及遇到的一些问题解决
-
OpenCV中的新函数connectedComponentsWithStats使用(python和c++实例)
-
一起talk C栗子吧(第一百八十四回:C语言实例--在printf函数中设置输出宽度三)
-
ffmpeg使用C语言sdk实现抽取视频中的视频数据
-
c语言:一组数据中只有一个数字出现了一次。(使用位运算)
-
Ubuntu下leveldb的安装使用(C++中)
-
在字符串中找出第一个只出现一次的字符。经典C语言例题
-
C++中的c_str()函数怎么使用?
-
分组在re模块中的使用以及使用正则表达式的技巧
-
深入浅解正则表达式在Java中的使用