如果用户熟悉Linux下的sed、awk、grep或vi,那么对正则表达式这一概念肯定不会陌生。由于它可以极大地简化处理字符串时的复杂度,因此现
在已经在许多Linux实用工具中得到了应用。千万不要以为正则表达式只是Perl、Python、Bash等脚本语言的专利,作为C语言程序员,用户同
样可以在自己的程序中运用正则表达式。 " R3 E2 R/ N. g1 m. j- f
标准的C和C++都不支持正则表达式,但有一些函数库可以辅助C/C++程序员完成这一功能,其中最著名的当数Philip Hazel的Perl-Compatible Regular Expression库,许多Linux发行版本都带有这个函数库。 - l! }; A# j0 W. g+ q& c
编译正则表达式 & i. n! H' w2 e' F
为了提高效率,在将一个字符串与正则表达式进行比较之前,首先要用regcomp()函数对它进行编译,将其转化为regex_t结构: ~# ~1 q% p! x8 b- p: x
int regcomp(regex_t *preg, const char *regex, int cflags);
. Y; z6 e$ o! k6 z& b2 B( [ 参数regex是一个字符串,它代表将要被编译的正则表达式;参数preg指向一个声明为regex_t的数据结构,用来保存编译结果;参数cflags决定了正则表达式该如何被处理的细节。 ) [8 F9 ^1 u/ `% J* C
如果函数regcomp()执行成功,并且编译结果被正确填充到preg中后,函数将返回0,任何其它的返回结果都代表有某种错误产生。
, h$ ~" [: {, C4 X3 A* u 匹配正则表达式 . O X( x5 I( \2 O: \3 S; e
一旦用regcomp()函数成功地编译了正则表达式,接下来就可以调用regexec()函数完成模式匹配:
) X" t& G2 S- l- ~. c3 _1 Z int regexec(const regex_t *preg, const char *string, size_t nmatch,regmatch_t pmatch[], int eflags); 7 u7 E, ]8 o! R- C
typedef struct {
; m0 R) D/ G8 g! s0 I% X regoff_t rm_so;
+ p9 n! M. k; } regoff_t rm_eo; 6 k5 d. I# k7 a# I
} regmatch_t; ' T6 j2 B% A. A$ Z
参数preg指向编译后的正则表达式,参数string是将要进行匹配的字符串,而参数nmatch和pmatch则用于把匹配结果返回给调用程序,最后一个参数eflags决定了匹配的细节。
c; [& i' ]3 W+ M. X 在调用函数
regexec()进行模式匹配的过程中,可能在字符串string中会有多处与给定的正则表达式相匹配,参数pmatch就是用来保存这些匹配位置的,
而参数nmatch则告诉函数regexec()最多可以把多少个匹配结果填充到pmatch数组中。当regexec()函数成功返回时,从
string+pmatch[0].rm_so到string+pmatch[0].rm_eo是第一个匹配的字符串,而从
string+pmatch[1].rm_so到string+pmatch[1].rm_eo,则是第二个匹配的字符串,依此类推。 , F3 Q6 X* M# c3 ], l9 A8 |
释放正则表达式
/ J \$ k D/ m& s9 S X 无论什么时候,当不再需要已经编译过的正则表达式时,都应该调用函数regfree()将其释放,以免产生内存泄漏。 ' ?' G* A2 J* {: @; b. C( v
void regfree(regex_t *preg);
$ ]- Y- |) Z6 O' x 函数regfree()不会返回任何结果,它仅接收一个指向regex_t数据类型的指针,这是之前调用regcomp()函数所得到的编译结果。
4 m/ v+ w( d6 G& _) t; K* R. Y
如果在程序中针对同一个regex_t结构调用了多次regcomp()函数,POSIX标准并没有规定是否每次都必须调用regfree()函数进行释
放,但建议每次调用regcomp()函数对正则表达式进行编译后都调用一次regfree()函数,以尽早释放占用的存储空间。 3 B0 y8 }; _- ~& L$ v1 V- o# {
报告错误信息
8 c% ^1 B* }: H: Z 如果调用函数regcomp()或regexec()得到的是一个非0的返回值,则表明在对正则表达式的处理过程中出现了某种错误,此时可以通过调用函数regerror()得到详细的错误信息。 + J+ _0 N" h) u2 w" V+ m, |" N
size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size); / G, E- s6 K5 h
参数errcode是来自函数regcomp()或regexec()的错误代码,而参数preg则是由函数regcomp()得到的编译结果,其目
的是把格式化消息所必须的上下文提供给regerror()函数。在执行函数regerror()时,将按照参数errbuf_size指明的最大字节
数,在errbuf缓冲区中填入格式化后的错误信息,同时返回错误信息的长度。
; t9 h( W& [, Z) P 应用正则表达式 4 b; f5 q3 u* W; N+ M
最后给出一个具体的实例,介绍如何在C语言程序中处理正则表达式。 * l2 u+ c' k9 k4 p Y
#include + T4 o& ~/ z3 ]' z9 Q! f
#include
+ h! v7 H0 \3 P9 b/ f #include
) u( [! F( I% O* Y, e; q/ U /* 取子串的函数 */
' A x( e( |* c- o static char* substr(const char*str, unsigned start, unsigned end) 6 E& S6 z# C; Q
{ # |5 f f9 l/ d' q
unsigned n = end - start;
0 r0 i" b! L) _2 @' ]* h6 W static char stbuf[256]; % @: R' g( b4 `5 E5 |" I" \7 j
strncpy(stbuf, str + start, n);
( v- p9 ?9 p4 Y0 \5 Z3 h. s6 s stbuf[n] = 0;
* G* X }& b3 s' p2 l8 q4 e return stbuf; 4 M3 F" `" c( j4 _/ U# q3 q
}
# w( _6 M1 X8 n$ `! d- b! W1 O /* 主程序 */
# D. @$ m3 [& f1 k# e# v int main(int argc, char** argv)
4 C. n3 O$ K7 B7 o5 T( t {
I1 W. D' C( T: N char * pattern;
% Y1 \ W% k: @! \* e7 U int x, z, lno = 0, cflags = 0; - m+ O$ O: V0 d
char ebuf[128], lbuf[256];
$ v, H, n: x. e |7 e. _$ A regex_t reg;
3 I V9 r2 e0 e" e regmatch_t pm[10];
% B/ M- ^0 f$ n; Y& F6 h const size_t nmatch = 10;
6 o# a5 c2 o* K& \( z /* 编译正则表达式*/
, A6 Q" q5 N4 I9 {2 D0 \) { y4 W pattern = argv[1]; 3 G' Q, |; P. l) N
z = regcomp(®, pattern, cflags);
4 z% i6 A3 G R/ ]( x- i' { if (z != 0){
* `0 K$ ?7 c z; g" |, T4 S& N regerror(z, ®, ebuf, sizeof(ebuf)); ) m' U& j7 O) _4 T$ O
fprintf(stderr, "%s: pattern '%s' \n", ebuf, pattern); 9 H) S9 |% S/ F" o% H- w
return 1;
' v, F) y, s) Q% Y( x! t }
' ~( ]6 M3 e3 p6 } /* 逐行处理输入的数据 */
: i& o v1 u& P$ y& J. O while(fgets(lbuf, sizeof(lbuf), stdin)) { 4 F! m7 a* b) i9 X7 C: }- T
++lno;
% K/ W3 @1 z# C" v* D if ((z = strlen(lbuf)) > 0 && lbuf[z-1] == '\n') ) z8 {# c$ m/ t: }# e# c
lbuf[z - 1] = 0;
0 E. r# E5 r4 G \ /* 对每一行应用正则表达式进行匹配 */ 5 w* x# o f# Y; o4 i6 g
z = regexec(®, lbuf, nmatch, pm, 0);
z/ J9 M* v$ t G. j# \' _) L if (z == REG_NOMATCH) continue; 0 @- z# J1 j' d. S" a9 y: y( {
else if (z != 0) { 6 n7 J: Z5 a* q/ g: |+ r, t
regerror(z, ®, ebuf, sizeof(ebuf));
. |8 o7 N3 ^+ {( c4 C! K9 F4 B7 } fprintf(stderr, "%s: regcom('%s')\n", ebuf, lbuf); 9 r$ g; `) [ Y
return 2;
E7 P3 G+ k0 P3 z% u } ) Z9 X* `: ^# _4 L! ^0 R
/* 输出处理结果 */
2 b4 C: O. p) m for (x = 0; x % w) a" O b0 q
if (!x) printf("%04d: %s\n", lno, lbuf); 5 V, G2 i6 q F: ?. D
printf(" $%d='%s'\n", x, substr(lbuf, pm[x].rm_so, pm[x].rm_eo));
8 r8 Z- m) E- M+ ^0 E% D: B A" h" M5 W } 1 ^. i! e. D/ J: A' x' o2 u8 m
} # O- r' ~& v8 {' `, H' ]" A" l0 ?
/* 释放正则表达式 */
2 u& c+ {* ]- I regfree(®);
4 ~" s' F% N& H6 ? return 0;
; Y7 H2 b- J* ]1 c } 3 e& a# [3 a$ t( g9 E3 d9 v6 n
上述程序负责从命令行获取正则表达式,然后将其运用于从标准输入得到的每行数据,并打印出匹配结果。执行下面的命令可以编译并执行该程序: ; z/ R1 S' x9 W x! U% M; ` Y
# gcc regexp.c -o regexp
2 T6 X. e V; n" ]2 k # ./regexp 'regex[a-z]*'
2 h8 y+ R" o7 z% A7 N; q 0003: #include
4 `2 c/ A, u+ N2 K7 t5 r; |4 n3 l) w $0='regex'
9 I; D+ A: O+ f& ^ 0027: regex_t reg; 8 G/ s6 ~, m# {% ?& q; I
$0='regex'
+ k$ z6 A3 I |1 J! w 0054: z = regexec(®, lbuf, nmatch, pm, 0); 2 R1 g+ P% O4 R- m7 [- X1 ] c
$0='regexec' 9 _( y. c- b! T1 s% F
小结
`* | K0 X/ q 对那些需要进行复杂数据处理的程序来说,正则表达式无疑是一个非常有用的工具。本文重点在于阐述如何在C语言中利用正则表达式来简化字符串处理,以便在数据处理方面能够获得与Perl语言类似的灵活性
阅读(907) | 评论(0) | 转发(0) |