Chinaunix首页 | 论坛 | 博客
  • 博客访问: 198282
  • 博文数量: 264
  • 博客积分: 6010
  • 博客等级: 准将
  • 技术积分: 2740
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-03 13:25
文章分类

全部博文(264)

文章存档

2011年(1)

2009年(263)

我的朋友

分类: C/C++

2009-06-04 13:51:44

如果用户熟悉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) |
给主人留下些什么吧!~~