正则表达式定义
正则表达式(regular expression)是Linux系统中一种非常重要的字符串搜索模式,是一组规则字符的集合。这些规则字符能够组成我们所需要的搜索规则,效率高、功能强,可以极大地简化处理字符串时的复杂度。在很多Linux工具(sed、grep、find等)和脚本语言(awk、perl等)中都有着重要的地位。当我们在编写字符串相关的应用程序时,掌握正则表达式会起到事半功倍的效果。
C中的正则表达式
标准C和C++都不支持正则表达式,但是千万不要以为正则表达式就只是Perl、Python、Bash等脚本语言的专利,作为C语言程序员,用户同样可以在自己的程序中运用正则表达式,只是需要一些函数库辅助C/C++程序员来完成这一功能。许多Linux发行版本都带有POSIX函数库,下面我将以POSIX函数库中的Regex系列函数来说明在Linux c下如何使用正则表达式。
首先要用Regcomp()函数对它进行编译,将其转化为Regex_t结构。因为一个正则表达式需要编译成一个特定的数据结构才能被后续的函数使用。Regcomp()函数的原型是:
int Regcomp(regex_t *preg, const char *regex, int cflags)
参数preg指向一个声明为regex_t的数据结构,用来保存编译结果。参数regex为要编译的正则表达式字符串。参数cflags是编译开关,编译开关可以控制规则生成的特性,如REG_EXTEND代表使用扩展正则表达式模式;REG_ICASE表示对规则中字符串不区分大小写;REG_NOSUB只检查是否有符合规则的子串。
下面匹配正则表达式,一旦用Regcomp()函数成功地编译了正则表达式,接下来就可调用regexec()函数完成模式匹配。Regexec()函数的原型:
int regexec(const regex_t *preg, const char *string, size_t nmatch,regmatch_t pmatch[], int eflags)
函数用于在字符串(参数string)中匹配正则表达式(参数preg)。而参数nmatch和pmatch则用于把匹配结果返回给调用程序。在调用函数egexec()进行模式匹配的过程中,可能在字符串string中会有多处与给定的正则表达式相匹配。参数pmatch用来保存这些匹配位置。参数nmatch则告诉函数regexec()最多可以把多少个匹配结果填充到pmatch数组中。
typedef struct {
regoff_t rm_so;
regoff_t rm_eo;
} regmatch_t;
其中rm_so表示满足规则的子串在string中的起始偏移量,rm_eo表示满足规则的子串在string中的后续偏移量。当regexec成功返回时,从pmatch[0].rm_so到pmatch[0].rm_eo是第一个匹配的字符串。最后一个参数eflags决定了匹配的特性,当需要匹配的string非常大的时候可以通过eflags来表示是否是第一行(REG_NOTBOL),或最后一行(REG_NOTEOL)。
还有一个函数是用来获取错误信息的,size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)。参数errcode是来自函数Regcomp()或regexec()的错误代码,而参数preg则是由函数Regcomp()得到的编译结果,其目的是把格式化消息所必须的上下文提供给regerror()函数。在执行函数regerror()时,将按照参数errbuf_size指明的最大字节数。在errbuf缓冲区中填入格式化后的错误信息,同时返回错误信息的长度。
最后释放正则表达式。无论什么时候,当不再需要已经编译过的正则表达式时,都应该调用函数regfree()将其释放,以免产生内存泄漏。void regfree(regex_t *preg); 函数regfree()不会返回任何结果,它仅接收一个指向regex_t数据类型的指针,这是之前调用Regcomp()函数所得到的编译结果。
编译正则表达式函数
#include <sys/types.h>
#include <regex.h>
int regcomp(regex_t *preg, const char *pattern, int cflags)
typedef struct{
int reg_magic;
size_t re_nsub;
const char *re_endp'
struct re_guts *re_g;
} regex_t;
pattern 正则表达式字符串
preg regex_t类型的结构变量, 调用放置编译结果的地方
cflags 下面一外或多个进行"或"操作的位掩码
REG_EXTENDED 使用POSIX延伸表示语法,否则使用POSIX基本语法
REG_NOSPEC 禁用所有的元字符,即不认为模式字符具有特殊意义
REG_ICASE 忽略大小写
REG_NOSUB 忽略参数nmatch和pmatch
REG_NEWLINE 不对特殊字符进行比较如^ . * ? + | / [ ] ( ) < >和$
REG_PEND 编译模式,在遇到第一个空字节时,正则表达式不会结束,正则表达式将在preg->re_endp指向的字节之前结束。 这就允许正则表达式中引入空字节
.正则表达式错误, 用来取得regcomp()或regexec()的错误原因
/**
* errorcode 为由regcomp()或regexec()返回的错误代码
* preg 指向pattern buffer的结构指针
* errbuf 指向欲存放错误字符串的缓冲区, errbuf_size为缓冲区大小
*
* 返回错误字符串的长度
*/
#include <sys/types.h>
#include <regex.h>
size_t regerror(int errorcode, const regex_t *preg,
char *errbuf, size_t errbuf_size);
.匹配正则表达式
#include <sys/types.h>
#include <regex.h>
/*
* 成功返回0
* 失败返回错误代码
*/
int regexec(const regex_t *preg, //指针,指向以前编译过的而且由regcomp(3)函数初始化的正则表达式
const char *string, //要匹配的字符串
size_t nmatch, //和pmatch把参数用于把匹配的模式返回给调用程序
regmatch_t pmatch[],
int eflags); //包含由0个或以上标志的"或"操作 (REG_NOTBO, REG_NOTEOL, REG_STARTEND)
typedef struct{
regoff_t rm_so; //符合条件的开始位置,如果值为-1,则代表此结构并未让regexec()使用
regoff_t rm_eo; //符合条件的结束位置 end of match offset
} regmatch_t;
参数eflags有两种可能,可使用OR(|)组合:
REG_NOTBOL 让特殊字符^无作用
REG_NOTEOL 让特殊字符$无作用
.释放正则表达式
#include <sys/types.h>
#include <regex.h>
void regfree(regex_t *preg);
释放regcomp已经编译的正则表达式
.示例
int z; //Error code
regex_t reg; //Compiled regexpr
char ebuf[128]; //Error message buffer
z = regcomp(®, pattern, REG_EXTENDED);
if(z != 0 )
{
regerror(z, ®, buf, sizeof(ebuf));
printf("%s: regcomp(3)/n", ebuf);
exit(1);
}
//.....
regfree(®);
示例:
#include <stdio.h>
#include <sys/types.h>
#include <regex.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int cns_reg(const char *str, const char *pattern)
{
int z; //status
int cflags = 0; //compile flags
regex_t reg; //compiled regular expression
//char *pattern; //正则表达式
char ebuf[128]; //error buffer
regmatch_t pm[10]; //pattern matches 0-9
const size_t nmatch = 10; //The size of array pm[]
//编译正则表达式
/**
*
* @param const char* pattern 将要被编译的正则表达式
* @param regex_t* reg 用来保存编译结果
* @param int cflags 决定正则表达式将如何被处理的细节
*
* @return success int 0 并把编译结果填充到reg结构中
* fail int 非0
*
*/
z = regcomp(®, pattern, cflags);
if(z != 0)
{
regerror(z, ®, ebuf, sizeof(ebuf));
fprintf(stderr, "%s: pattern '%s'/n", ebuf, pattern);
return 1;
}
//report the number of subexpressions
if(!(cflags & REG_NOSUB))
printf("There were %d subexpression./n", reg.re_nsub);
/**
*
* reg 指向编译后的正则表达式
* str 指向将要进行匹配的字符串
* pm str字符串中可能有多处和正则表达式相匹配, pm数组用来保存这些位置
* nmacth 指定pm数组最多可以存放的匹配位置数
*
* @return 函数匹配成功后,str+pm[0].rm_so到str+pm[0].rm_eo是第一个匹配的子串
* str+pm[1].rm_so到str+pm[1].rm_eo是第二个匹配的子串
* ....
*/
z = regexec(®, str, nmatch, pm, 0);
if(z == REG_NOMATCH)
return 1;
else if(z != 0)
{
regerror(z, ®, ebuf, sizeof(ebuf));
fprintf(stderr, "%s: regcomp('%s')/n", ebuf, str);
return 2;
}
regfree(®);
return 0;
}
int main(int argc, char **argv)
{
printf("%d/n", cns_reg(argv[1], argv[2]));
return 0;
}
.运行
[root@localhost sockets]# ./reg "I love you" "^I.*"
There were 0 subexpression.
0
[root@localhost sockets]# ./reg "I love you" "I.*"
There were 0 subexpression.
0
[root@localhost sockets]# ./reg "I love you" "dI.*"
There were 0 subexpression.
1
[root@localhost sockets]# ./reg "I love you" "//"
There were 0 subexpression.
1
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" "//"
There were 0 subexpression.
0
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" "http://"
There were 0 subexpression.
0
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" "fttp://"
There were 0 subexpression.
1
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" ""
There were 0 subexpression.
0
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" "."
There were 0 subexpression.
0
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" ".."
There were 0 subexpression.
0
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" "/./."
There were 0 subexpression.
1
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" ".."
There were 0 subexpression.
0
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" "..*"
There were 0 subexpression.
0
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" "[a-z]3"
There were 0 subexpression.
1
[root@localhost sockets]# ./reg "http://www.800hr.com/main.php" "[a-z]3.*"