正则表达式使用简介

2021-07-24  本文已影响0人  Jamza

基本概念

字符 含义 举例
. 匹配任意一个字符 如“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;
}

上一篇下一篇

猜你喜欢

热点阅读