Chinaunix首页 | 论坛 | 博客
  • 博客访问: 89334
  • 博文数量: 87
  • 博客积分: 3980
  • 博客等级: 中校
  • 技术积分: 1010
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-08 06:42
文章分类

全部博文(87)

文章存档

2010年(1)

2009年(86)

我的朋友

分类: LINUX

2009-07-30 10:55:55

  一个正则表达式的教程可以参看(里面有个测试正则表达式的工具)

    

 

     正则表达是用来匹配字符串的好东东。
  
     如果用户熟悉Linux下的sed、awk、grep或vi,那么对正则表达式这一概念肯定不会陌生。由于它可以极大地简化处理字符串时的复杂度,因此现在已经在许多Linux实用工具中得到了应用。千万不要以为正则表达式只是Perl、Python、Bash等脚本语言的专利,作为C语言程序员,用户同样可以在自己的程序中运用正则表达式。
   
    标准的C和C++都不支持正则表达式,但有一些函数库可以辅助C/C++程序员完成这一功能,其中最著名的当数Philip Hazel的Perl-Compatible Regular Expression库,许多Linux发行版本都带有这个函数库。
   
    本人使用的是fedora 8系统,该版本中就带有这个函数库,下面介绍一下如何使用。(实际上只要使用man命令查询就可了解很多有关的细节了)
   
    很多在网上都有介绍,但多有些小的错误,于是我更改了并整理了一下。

    要使用正则表达式的函数库在你的程序前面包含
    #include
    #include
    
    下面介绍下如何使用:
    首先,编译正则表达式。
    为了提高效率,在将一个字符串与正则表达式进行比较之前,首先要用regcomp()函数对它进行编译,将其转化为regex_t结构:
   
    int regcomp(regex_t *preg, const char *regex,int cflags);
   
    参数regex是一个字符串,它代表将要被编译的正则表达式;参数preg指向一个声明为regex_t的数据结构,用来保存编译结果;参数cflags决定了正则表达式该如何被处理的细节。(此处可以用man regcomp 命令查看详细的解释)
    如果函数regcomp()执行成功,并且编译结果被正确填充到preg中后,函数将返回0,任何其它的返回结果都代表有某种错误产生。
   
    接下来匹配正则表达式。
    一旦用regcomp()成功地编译了正则表达式,接下来就可以调用regexec()完成模式匹配:

    int regexec(const regex_t *preg, const char *string, size_t nmatch,regmatch_t pmatch[], int eflags);
    typedef struct
    {
        regoff_t rm_so;
        regoff_t rm_eo;
    } regmatch_t;   

    参数preg指向编译后的正则表达式,参数string是将要进行匹配的字符串,而参数nmatch和pmatch则用于把匹配结果返回给调用程序,最后一个参数eflags决定了匹配的细节。

    在调用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,则是第二个匹配的字符串,依此类推。

    最后释放正则表达式。
    无论什么时候,当不再需要已经编译过的正则表达式时,都应该调用regfree()将其释放,以免产生内存泄漏。
    void regfree(regex_t *preg);

    报告错误信息
  
如果调用regcomp()或regexec()得到的是一个非0的返回值,则表明在对正则表达式的处理过程中出现了某种错误,此时可以通过调用regerror()得到详细的错误信息。

    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缓冲区中填入格式化后的错误信息,同时返回错误信息的长度。



    给出一个应用正则表达式的例子
    (这个例子网上容易找到,但多半有些小错误,不能编译成功,我将其修改写在了下面)

例子如下:

#include
#include
#include




static char* substr(const char*str,unsigned start, unsigned end)
{
    unsigned n = end - start;

    static char stbuf[256];

    strncpy(stbuf, str + start, n);

    stbuf[n] = 0;

    return stbuf;

}

int main(int argc, char** argv)
{
    char * pattern;
    int x, z, lno = 0, cflags = 0;
    char ebuf[128], lbuf[256];
    regex_t reg;
    regmatch_t pm[10];
    const size_t nmatch = 10;

   

    pattern = argv[1];
    z = regcomp(®, pattern, cflags);

    if (z != 0)
     
        regerror(z, ®, ebuf, sizeof(ebuf));
        fprintf(stderr, "%s: pattern '%s' \n",ebuf, pattern);
        return 1;
    }

   

    while(fgets(lbuf, sizeof(lbuf), stdin))
    {
        ++lno;
        if ((z = strlen(lbuf)) > 0 && lbuf[z-1]== '\n') lbuf[z - 1] = 0;    

       
        z = regexec(®, lbuf, nmatch, pm, 0);
        if (z == REG_NOMATCH) continue;
        else if (z != 0)
        {
            regerror(z, ®, ebuf, sizeof(ebuf));
            fprintf(stderr, "%s: regcom('%s')\n",
            ebuf, lbuf);
            return 2;
        }

       

        for (x = 0; x < nmatch && pm[x].rm_so != -1; ++ x)
        {
            if (!x) printf("%04d: %s\n", lno, lbuf);
            printf(" $%d='%s'\n", x, substr(lbuf, pm[x].rm_so,pm[x].rm_eo));
        }

    }

   

    regfree(®);    
    return 0;
}

上述程序负责从命令行获取正则表达式,然后将其运用于从标准输入得到的每行数据,并打印出匹配结果。执行下面的命令可以编译并执行该程序:
    # gcc regexp.c -o regexp (编译是会有两个警告,没有事)
    # ./regexp 'regex[a-z]*' < regexp.c
   
    0003: #include
    $0='regex'
    0027: regex_t reg;
    $0='regex'
    0054: z = regexec(?, lbuf, nmatch, pm, 0);
    $0='regexec'

小结
    在C语言中使用正则表达式并不复杂。
    正则表达式用来匹配复杂的字符串非常好用。
---------------------------------------------------------------------------

附:

1.int   regcomp   (regex_t   *compiled,   const   char   *pattern,   int   cflags)  
   
  这个函数把指定的规则表达式pattern编译成一种特定的数据格式compiled,这样可以使匹配更有效。函数regexec   会使用这个数据在目标文本串中进行模式匹配。执行成功返回0。  
   
  regex_t   是一个结构体数据类型,用来存放编译后的规则表达式,它的成员re_nsub   用来存储规则表达式中的子规则表达式的个数,子规则表达式就是用圆括号包起来的部分表达式。  
   
  pattern   是指向我们写好的规则表达式的指针。  
  cflags   有如下4个值或者是它们或运算(|)后的值:  
  REG_EXTENDED   以功能更加强大的扩展规则表达式的方式进行匹配。  
  REG_ICASE   匹配字母时忽略大小写。  
  REG_NOSUB   不用存储匹配后的结果。  
  REG_NEWLINE   识别换行符,这样'$'就可以从行尾开始匹配,'^'就可以从行的开头开始匹配。  
   
  2.   int   regexec   (regex_t   *compiled,   char   *string,   size_t   nmatch,   regmatch_t   matchptr   [],   int   eflags)  
   
  当我们编译好规则表达式后,就可以用regexec   匹配我们的目标文本串了,如果在编译规则表达式的时候没有指定cflags的参数为REG_NEWLINE,则默认情况下是忽略换行符的,也就是把整个文本串当作一个字符串处理。执行成功返回0。  
   
  regmatch_t   是一个结构体数据类型,成员rm_so   存放匹配文本串在目标串中的开始位置,rm_eo   存放结束位置。通常我们以数组的形式定义一组这样的结构。因为往往我们的规则表达式中还包含子规则表达式。数组0单元存放主规则表达式位置,后边的单元依次存放子规则表达式位置。  
   
  compiled   是已经用regcomp函数编译好的规则表达式。  
  string   是目标文本串。  
  nmatch   是regmatch_t结构体数组的长度。  
  matchptr   regmatch_t类型的结构体数组,存放匹配文本串的位置信息。  
  eflags   有两个值  
  REG_NOTBOL   按我的理解是如果指定了这个值,那么'^'就不会从我们的目标串开始匹配。总之我到现在还不是很明白这个参数的意义,   原文如下:  
  If   this   bit   is   set,   then   the   beginning-of-line   operator   doesn't   match   the   beginning   of   the   string   (presumably   because   it's   not   the   beginning   of    line).If   not   set,   then   the   beginning-of-line   operator   does   match   the   beginning   of   the   string.  
  REG_NOTEOL   和上边那个作用差不多,不过这个指定结束end   of   line。  
   
  3.   void   regfree   (regex_t   *compiled)  
   
  当我们使用完编译好的规则表达式后,或者要重新编译其他规则表达式的时候,我们可以用这个函数清空compiled指向的regex_t结构体的内容,请记住,如果是重新编译的话,一定要先清空regex_t结构体。  
   
  4.   size_t   regerror   (int   errcode,   regex_t   *compiled,   char   *buffer,   size_t   length)  
   
  当执行regcomp   或者regexec   产生错误的时候,就可以调用这个函数而返回一个包含错误信息的字符串。  
   
  errcode   是由regcomp   和   regexec   函数返回的错误代号。  
  compiled   是已经用regcomp函数编译好的规则表达式,这个值可以为NULL。  
  buffer   指向用来存放错误信息的字符串的内存空间。  
  length   指明buffer的长度,如果这个错误信息的长度大于这个值,则regerror   函数会自动截断超出的字符串,但他仍然会返回完整的字符串的长度。所以我们可以用如下的方法先得到错误字符串的长度。  
  size_t   length    regerror   (errcode,   compiled,   NULL,   0);  

阅读(751) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~