代码改变世界

C语言正则表达式

2010-08-28 11:17  htc开发  阅读(327)  评论(0编辑  收藏  举报

在很多人看来,正则表达式看上去古怪且复杂,因此往往不想去触及或使用。如果我们抛开这种“畏难”心理,在经过循序渐进地学习之后,就会发现这些复杂的表达式其实写起来还是相当简单的。一旦它被你所“驯服”,就可利用正则表达式把原需数小时辛苦且易错的文本处理工作压缩在几分钟(甚至几秒钟)内完成。

 

正则表达式定义
正则表达式(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()函数所得到的编译结果。

实例说明
下面介绍如何在Linux C中正确使用正则表达式,以查找有关字符串并打印出来为例:
#include <stdio.h>
#include <sys/types.h>
#include <regez.h>

in chk_line(int lineno, regex_t *reg,char *line)
{
int rtn,i,len;
regmatch_t pmatch;
char *url,*pbuf;

fprintf(stderr,"%4d",lineno);
rtn = regexec(reg,line,1,&pmatch,0);
pbuf = line;
while(rtn == 0)
{
len = pmatch.rm_eo - pmatch.rm_so;
url = (char*)malloc((len+1)*sizeof(char));
memset(url,0,(len+1)*sizeof(char));
memcpy(url,&pbuf[pmatch.rm_so].len);
fprintf(stderr,"%s",url);
free(url);
pbuf += pmatch.rm_eo;
rtn = regexec(reg,pbuf,1,&pmatch,REG_NOTBOL);
}
fprintf(stderr,"/n");
return 0;
}
int chk_file(const char *filename)
{
FILE *fp;
char *pattern = "^(hisencyber)(.com|.com.cn)";
char buf[1024],line[1024];
int rtn,lineno,flag;

fp = fopen(filename,"r");
if(fp == NULL)
{
fprintf(stderr,"OPen file failed/n",filename);
return -1;
}
rtn = Regcomp(®,patten,REG_ICASE|REG_EXTENDED);
if(rtn)
{
fprintf(stderr,"compile failed./n");
fclose(fp);
return -1;
}
lineno = 1;
memset(line,0,sizeof(line));
while(fgets(line,sizeof(line),fp)!= NULL)
{
chk_line(lineno++,®,line);
}
fclose(fp);
regefree(®);
return 0;
}
int main (int argc,char *argv[])
{
int rtn;
if(argc != 2)
{
fprintf(stderr,"Usage:chkfileurl <file> /n ");
return 1;
}
rtn = chk_file(argv[1]);
return rtn;
}

正则表达式示例表
字 符 意 义 示 例
* 任意长度的字符串。 a* 表示: 空字符串、aaaa、a…
? 长度为0或者1的字符串。 a? 表示: 空字符串和a。
+ 长度为一个或者多个的字符串。 a+表示:a、aa、aaaaaa…
. 任意字符。 a. 表示:a后跟任意字符。
{} 代表上一规则重复数目、
{1,1,s}包含一组匹配花括号,里面有两个数字和一个字符,表示在指定次数范围内找到字符。 a{3}表示:三个a、
a{1,3}表示:一个到三个a、
a{3,} 表示:大于等于三个a、
{3,7,a}表示在3到7次重复范围内匹配字符a。
[] 集合,代表方括号中任意一个字符。 [ab] 表示:a或者b都可以、
[a-z] 表示:从a到z的字符。
() 组,代表一组字符。 (ab){2}表示:abab。
a/b 同时满足。 a/b表示:字符串a后跟字符串b才能满足要求。
a|b 并列,代表符合a或者符合b都可以 a|b表示: 字符串a或者字符串b都满足要求。
^ 如果放在开头表示代表该规则必须在字符串的开头,其他位置代表字符本身。
如果放在[]中的开头表示对该集合取反,其他位置代表字符本身。 ^a表示:a必须在字符串的开头、
[^a]表示:除了a以外的其他字符。
$ 如果放在最后表示该规则必须放在最后,其他位置代表字符本身。 a$表示:a必须在字符串最后。
/:s 正则表达式用 /:s 表示空格。 a/:sb 匹配 a b。
/:a 正则表达式用 /:a 表示字符与数字。 a/:a 匹配 ab、a6 等。
/:c 正则表达式用 /:c 仅表示字符。 a/:c 匹配 ac等,不匹配a1等。
/:p 正则表达式用 /:p 表示可打印字符。 
/:D 正则表达式用 /:d 仅表示数字。 a/:c 匹配 a1等,不匹配ac等。
/:x00 正则表达式用 /:x00 表示ASCII字符。 
/:r 正则表达式用 /:r 表示回车。 
/:N 正则表达式用 /:d 表示换行。

本文重点在于阐述如何在Linux C中利用正则表达式来简化字符串处理,另外Linux中正则表达式内部定义了诸多内部规则来方便开发人员使用,具体含义可以查看 man regex,相信熟悉它对你的工作会有非常大的帮助!