正则表达式(regex)及C语言实现,超强查找/匹配/替换算法

 

DOS/Windows的文件名通配符,比如:*.txt,我们可能很熟悉。介绍一个更强的—正则表达式。

 

一、正则表达式
正则表达式(regular expression,或简称regex ),是一种字符串查找的语言,一个正则表达式,就是用某种模式去匹配一类字符串的一个公式。

举几个例子吧:
   正则表达式 “r.t”,其中 . 表示任意一个字符(如同DOS的通配符 ?) ,可以在字符串 “rat rot rpt”中匹配出 rat rot rpt
   正则表达式 “r[aou]t”,可以在字符串 “rat rot rpt”中匹配出 rat rot,但不能匹配出rpt,因为表达式中规定第二个字符只能是a,o,u

正则表达式这个概念最初是由Unix中的工具软件(例如sed、grep及vi)普及开的。
很多人因为它们看上去比较古怪而且复杂所以不敢去使用,但它的功能很强。
在Unix中,用grep,可以用一个正则表达式一次性对文件进行批量文字操作,比如:删除空格行、更改函数参数等等,够强不。
关于它的详细解释和用途,大家可以在网上搜一下,很多。

 

二、C语言实现
虽然很多开发工具都提供了正式表达式的支持库。但我还是想自己整一个。
在网上找到著名的Henry Spencer的regex library源码(http://www.arglist.com/regex/files/regexp.old.tar.Z)。
Henry Spencer 1986年第一次写的,用纯C写的,很好移植,很多后来的regex实现都参考它。

库中有几个函数和数据结构,用起来有一些麻烦,要花一段时间去摸索。
于是,我把它包装了一下,以方便使用。就二个纯C文件(JRegex.h,JRegex.c),可移值,不依赖于任何第三方代码。

其中:开发了几个非常简单的函数:

 

//查找函数, 用正则表达式regex_expression,int 在string中匹配查找
//找到则返回1, start是找到的开始的位置,length是找到的长度
int RegexFind(char *string, char * regex_expression,int *start, int *length);

 

//替换函数, 用正则表达式regex_expression,int 在string中匹配查找
//找到用replace_regex_expr,替换,start是找到的开始的位置,length是替换的长度
//成功则返回1,失败返回0
int RegexReplace(char *string,char * find_regex_expr,char * replace_regex_expr, int *start,int *length);

 

//从string中抽取子串,子串从start开始,长度为length
//此函数用malloc()生成返回字符串所需的内存,用完后记得要free()
char * ExtractString(char *string, int start, int length);

 

用法很简单:

(一)Regex查找示例
    char *string;
    char *find_regex_expr;      //查找用的正则表达式
    char *find_string;          //查找到的子串
    int start;                  //查找到的子串的起始位置
    int length;                 //子串的长度

    string=malloc(256);
    strcpy(string,”rat rot rpt”);
    find_regex_expr=”r[aou]t”;     //意思是三个字符r?t, 第二个字符只能是a,o,u之一

    printf(“RegexFind Example: string=%s, regex=%s/n”,string,find_regex_expr);

    //Regex查找示例
    while (1==RegexFind(string,find_regex_expr,&start, &length))
      {
         //找到后,从string截取出子串
         find_string=ExtractString(string, start, length);
         if (find_string!=NULL)
           {
              printf(“Found %s/n”, find_string);
              free(find_string); //由于ExtractString时申请了内存,此处释放子串内存
           }
         string=string+length; //将指针指向下一个位置,以便继续查找
      }

 

返回结果为:
RegexFindExample: string=rat rot rpt, find_regex=r[aou]t
Found rat
Found rot

(二)替换示例         
    strcpy(string,”rat rot rpt”);
    old_string=string;               //保存字串的起始位置
    find_regex_expr=”r([aou])t”;     //意思是三个字符r?t, 第二个字符只能是a,o,u之一
                                                      // 其中 ( ) 的含义是把其中找到的字符编成一个组,在替换中可用 /1 来获得
    replace_regex_expr=”r//1//1t”;   //替换为 r??t, 把第二个字符重复一遍

    printf(“RegexReplace Example: string=%s, find_regex=%s, replace_regex=%s/n”,
            string,find_regex_expr,replace_regex_expr);

    while (1==RegexReplace(string,find_regex_expr,replace_regex_expr,&start, &length))
      {
         //找到后,从string截取出子串
         find_string=ExtractString(string, start, length);
         if (find_string!=NULL)
           {
              printf(“Replace with %s/n”, find_string);
              free(find_string); //由于ExtractString时申请了内存,此处释放子串内存
           }
         string=string+length; //将指针指向下一个位置,以便继续替换
      }

    printf(“String after replace: %s”,old_string); //显示完成替换后的字串

 

运行结果为:  
RegexReplace Example: string=rat rot rpt, find_regex=r([aou])t, replace_regex=r/1/1t
Replace with raat
Replace with root
String after replace: raat root rpt

 

三、DOS/Windows的文件名通配符

文件名通配符 ? 和 * , 大家可能很熟悉了。如何在程序用实现通配符的功能、实现文件名的筛选呢?
这里介绍一个利用正则表达式的实现方式。

原理: 把包含通配符 ? 和 * 转换为等价的正则表达式,搜索即可。
比如:  a?b.txt   转化为正则表达式  a.b/.txt
              *.txt       转化为正则表达式  .*/.txt
具体来说:
   * 变成 .*
   ? 变成 .
   特殊字符,如:, / / { } ( )等,前面加一个 / 号。

 

我的JRegex.c中包装了两个函数:

 

//转换函数, 将通配符表达式转换为正则表达式
//此函数用malloc()生成返回字符串所需的内存,用完后记得要free()
char *WildCharToRegex(char *wild_char_string);

 

//通配符查找函数,用通配符表达式find_expression在string中匹配查找
//找到则返回1, start是找到的开始的位置,length是找到的长度
//事实上,这个函数是先调用WildCharToRegex,再调用RegexFind。
int DosWildCharFind(char *string, char * find_expression,int *start, int *length);

 

(一)转换示例

   char *string;
   char *find_expression;    //查找用的通配符表达式
   char *regex_expression;   //正则表达式
   int start;                  //查找到的子串的起始位置
   int length;                 //子串的长度  

   string=malloc(256);
   strcpy(string,”hello.txt”);
   find_expression=”*.txt”;

   //转换为regex的示例
   regex_expression=WildCharToRegex(find_expression);
   printf(“/nWildchar Expression: %s /n”,find_expression);
   printf(“Regex Expression: %s /n”,regex_expression);
   free(regex_expression); //由于WildCharToRegex时申请了内存,此处释放内存

(二)查找/匹配示例

   //直接用通配符表达式查找/匹配
   printf(“String %s /n”,string);
   if (1==WildCharFind(string, find_expression, &start, &length))
     {
       printf(“Found %s at %d  /n”,find_expression,start);
     }
   else
     {
        printf(“Not find  %s /n”,find_expression);
     }

 

运行结果为:  
Wildchar Expression: *.txt
Regex Expression:  .*/.txt
String hello.txt
Found *.txt at 0

四、应用软件对正则表达式的支持

Unix是正则表达式的原发地,grep, vi, sed, perl 都支持正则表达式。

Microsoft Word, Excel是不支持正则表达式的,它们支持一些特定的表达符,比如: ^p表达
这个实现起来很简单,就是在查找串中把 ^p 换成换行符即可。

VS.net支持正则表达式。

五、小结

正则表达式是一个很强的工具。有了它,可以在你的软件中增强查找、替换、匹配等功能。
本文提供的 JRegex.c 封装了Henry Spencer的Regex库(ANSI C),可移植到任何系统上。
为尊重原作者的版权要求,请在使用时声明 源码来源于Henry Spencer, 由JoStudio封装。

BLOG主页: http://blog.csdn.net/c80486

具体源码可在我的资源库中下载

 

    原文作者:查找算法
    原文地址: https://blog.csdn.net/c80486/article/details/6568582
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞