美文网首页
正则表达式使用简介

正则表达式使用简介

作者: Jamza | 来源:发表于2021-07-24 13:44 被阅读0次

    基本概念

    字符 含义 举例
    . 匹配任意一个字符 如“abc.”,可以匹配 abc2、abcd、abc% 等
    [] 匹配括号中的任意一个字符 如“[abc]d”,可以匹配 ad、bd、cd
    - 在[]括号范围内表示范围 如“[0-9]”,表示匹配 0 至 9 中任意一个数字
    ^ 位于[]内开头,匹配除括号内字符以外的任意字符 如“[^ab]”,表示匹配除了 a、b 之外的其他字符
    ? 紧跟其前面的单元匹配零次或者一次
    + 紧跟其前面的单元匹配一次或者多次
    * 紧跟其前面的单元匹配零次或者多次
    {N} 紧跟其前面的单元匹配精确的 N 次 如“k{3}”,表示精确匹配 kkk。
    {N,} 紧跟其前面的单元匹配至少 N 次
    {,M} 紧跟其前面的单元匹配最多 M 次
    {N,M} 紧跟其前面的单元匹配至少 N 次,最多 M 次
    ^ 匹配行首的位置
    $ 匹配行尾的位置
    \< 匹配单词开头的位置
    \> 匹配单词结尾的位置
    \b 匹配单词的边界 如“\bA.{3}T\b”,表示匹配类似 A123t、AXYZT 等单词
    \B 匹配非单词边界 如“\Bh\B”,表示匹配的 h 不在单词边界,如 host 则不符合,因为 h 在单词的边界,而 she 则符合匹配。
    | 连接两个子表达式,表示或的关系 如“n(ei|mn)”,匹配 nei 或者 nmn
    () 将正则表达式部分组成分组,可引用分组
    \ 转义字符
    \d 匹配数字字符,效果同[0-9]
    \D 匹配非数字字符,效果同[0-9],或者同[\d]
    \w 匹配单词字符,效果同[_a-zA-Z0-9]
    \W 匹配非单词字符
    \s 匹配空白字符,效果同[ \t\n\r],注意括号中包括空格
    \S 匹配非空白字符
    () 分组、子模式(subpattern)
    \1、\2... 通过后向引用重用捕获内容 如“(the)(china people) \2 \1”,这里的第一个分组是 the、第二个分组是 china people,则\1 表示第一个分组,\2 表示第二个分组。
    (?:name) 非捕获分组 如“(?:the|THE)”,非捕获分组不会将其存储在内存,无法后向引用

    C 语言实现正则表达式

    标准的 C 与 C++ 都不支持正则表达式,但是在某些场景下,正则表达式存在可以为程序带来便利。

    在 C 语言中,一些库函数可以帮助我们实现在 C 中使用正则表达式的诉求。

    以下介绍在 Linux 环境下 C 语言中处理正则表达式的常用函数。

    regcomp 函数

    函数原型为:

    #include<regex.h>
    int regcomp(regex_t *compiled, const char *pattern, int cflags);
    

    函数作用:将指定的正则表达式格式 pattern 编译成一种特定的数据格式 compiled,这样可以使得正则表达式的匹配更加有效。函数执行成功返回 0。

    参数说明:

    1. regex_t 是一个结构体数据类型,用来存放编译后的正则表达式,其成员 re_nsub 用来存储正则表达式组合的子正则表达式的个数。

    2. pattern 指向要编译的正则表达式字符串。

    3. cflags 是标志位。可以取值为:

      a、REG_EXTENDED:以功能更加强大的扩展正则表达式的方式进行匹配

      b、REG_ICASE:匹配字母时,忽略大小写;

      c、REG_NOSUB:不用存储匹配后的结果;

      d、REG_NEWLINE:识别换行符,则符号 ^ 与 $ 可以分别从行首与行尾开始匹配。

    regexec 函数

    函数原型为:

    #include<regex.h>
    int regexec(regex_t *compiled, char *string, size_t nmatch, regmatch_t matchptr[], int eflags);
    

    函数作用:使用正则表达式执行匹配目标文本。如果在调用函数 regcomp 编译正则表达式时,没有指定 cflags 标志位为 REG_NEWLINE,则默认情况下忽略换行符,即将整个文本内的字符串当作一个整体来处理。函数执行成功返回 0。

    参数说明:

    1. compiled 是使用函数 regcomp 编译好的正则表达式;
    2. string 是需要匹配的目标字符串;
    3. nmatch 是 regmatch_t 结构体数组的长度;
    4. matchptr 是 regmatch_t 类型的结构体数组,存放匹配字符串的位置信息;
    5. eflags 取值为 REG_NOTBOL,即让特殊字符 ^ 无作用,eflags 取值为 REG_NOTEOL,即让特殊字符 $ 无作用。

    regmatch_t 是一个结构体数据类型:

    typedef struct {
        regoff_t rm_so;     /* 存放匹配字符串在目标字符串中的起始位置 */
        regoff_t rm_eo;     /* 存放匹配字符串在目标字符串中的结束位置 */
    } regmatch_t;
    /* 通常以数组的形式定义一组regmatch_t结构,因为往往正则表达式中还包含子正则表达式,即存在分组
       数组0单元存放主正则表达式的位置
       数组1、2、3...依次存放子正则表达式的位置 */
    

    regfree 函数

    函数原型为:

    #include<regex.h>
    void regfree(regex_t *compiled);
    

    函数作用:使用完编译好的正则表达式后,或者需要重新编译其他的正则表达式时,需要使用函数 regfree 清空 compiled 指向的结构体内容。

    regerror 函数

    函数原型为:

    #include<regex.h>
    size_t regerror(int errcode, regex_t *compiled, char *buffer, size_t length);
    

    函数作用:当执行 regexec 或者 regcomp 产生错误的时候,可以调用函数 regerror 以返回一个包含错误信息的字符串。

    参数说明:

    1. errcode 是由函数 regexec 或者 regcomp 返回的错误代号;
    2. compiled 是已经编译好的正则表达式,其值可以为 NULL;
    3. buffer 指向用来存放错误信息的字符串内存空间;
    4. length 指明 buffer 的长度,如果错误信息的长度大于该值,则函数自动截断超出的字符串。

    范例

    #include<stdio.h>
    #include<sys/types.h>
    #include<regex.h>
    
    int my_match(char* pattern,char* buf){
      int status,i;
      int flag=REG_EXTENDED;
      regmatch_t pmatch[1];
      const size_t nmatch=1;
      regex_t  reg;
      //编译正则模式
      regcomp(&reg,pattern,flag);
      //执行正则表达式和缓存的比较
      status=regexec(&reg,buf,nmatch,pmatch,0);
      //打印匹配的字符串
      for(i=pmatch[0].rm_so;i<pmatch[0].rm_eo;++i){
        putchar(buf[i]);
      }
      printf("\n");
      regfree(&reg);
      return status;
    }
    
    int main(){
      char pattern[1024]="^[1-9][0-9]{10}$";
      char buf[1024]="41509030127";
      int status=my_match(pattern,buf);
      if(status==REG_NOMATCH)
        printf("No match!\n");
      else if(0 == status){
        printf("match!\n");
      }
      return 0;
    }
    

    C 语言中正则表达式的封装

    在 C 语言中,可以对正则表达式相关的库函数进行封装,以实现方便的个人自定义接口。

    封装的头文件定义了相关的结构体与宏:

    typedef struct
    {
        int start;
        int end;
    } T_ReReg;
    
    #define FILL_REG(buf, size, string, reg) do { \
        if (VALID_REG(reg)) { \
            snprintf(buf, size, "%*.*s", LEN_REG(reg), LEN_REG(reg), &string[reg.start]);} \
        else { \
            snprintf(buf, size, "");} \
    } while(0)
    #define LEN_REG(reg) (reg.end - reg.start)
    #define VALID_REG(reg) (reg.start >= 0 && reg.end >= reg.start)
    
    #define re_free(p) free(p)
    

    封装的函数定义如下:

    static void re_free_registers(struct re_pattern_buffer *buffer, struct re_registers *regs)
    {
        if ( NULL != buffer && REGS_REALLOCATE == buffer->regs_allocated)
        {
            if (NULL != regs)
            {
                if (NULL != regs->start)
                {
                    re_free(regs->start);
                    regs->start = NULL;
                }
                if (NULL != regs->end)
                {
                    re_free(regs->end);
                    regs->end = NULL;
                }
            }
            buffer->regs_allocated = REGS_UNALLOCATED;
        }
    }
    
    /* 通常以数组的形式定义一组T_ReReg结构,因为往往正则表达式中还包含子正则表达式,即存在分组
       数组0单元存放主正则表达式的位置
       数组1、2、3...依次存放子正则表达式的位置 */
    int cp_reMatch(const char *pattern, const char *string, T_ReReg *reRegs, unsigned int *regNum)
    {
        struct re_pattern_buffer regex;
        const char *err = NULL;
        int num = -1;
    
        re_set_syntax(RE_SYNTAX_POSIX_EGREP);
        memset(&regex, 0, sizeof(regex));
        err = re_compile_pattern(pattern, strlen(pattern), &regex);
        if (NULL == err)
        {
            struct re_registers regs;
            memset(&regs, 0, sizeof(regs));
            re_set_registers(&regex, &regs, 0, NULL, NULL);
            num = re_match(&regex, string, strlen(string), 0, &regs);
    
            /* fill regular expression registers */
            if ((NULL != reRegs) && (NULL != regNum) && ((*regNum) > 0))
            {
                unsigned int i;
                for (i = 0; (i < regs.num_regs) && (i < *regNum); i++)
                {
                    reRegs[i].start = regs.start[i];
                    reRegs[i].end = regs.end[i];
                }
                *regNum = i;
            }
            re_free_registers(&regex, &regs);
            regfree(&regex);
        }
        return num;
    }
    
    

    相关文章

      网友评论

          本文标题:正则表达式使用简介

          本文链接:https://www.haomeiwen.com/subject/nplemltx.html