正则表达式使用简介
基本概念
字符 | 含义 | 举例 |
---|---|---|
. | 匹配任意一个字符 | 如“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。
参数说明:
-
regex_t 是一个结构体数据类型,用来存放编译后的正则表达式,其成员 re_nsub 用来存储正则表达式组合的子正则表达式的个数。
-
pattern 指向要编译的正则表达式字符串。
-
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。
参数说明:
- compiled 是使用函数 regcomp 编译好的正则表达式;
- string 是需要匹配的目标字符串;
- nmatch 是 regmatch_t 结构体数组的长度;
- matchptr 是 regmatch_t 类型的结构体数组,存放匹配字符串的位置信息;
- 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 以返回一个包含错误信息的字符串。
参数说明:
- errcode 是由函数 regexec 或者 regcomp 返回的错误代号;
- compiled 是已经编译好的正则表达式,其值可以为 NULL;
- buffer 指向用来存放错误信息的字符串内存空间;
- 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(®,pattern,flag);
//执行正则表达式和缓存的比较
status=regexec(®,buf,nmatch,pmatch,0);
//打印匹配的字符串
for(i=pmatch[0].rm_so;i<pmatch[0].rm_eo;++i){
putchar(buf[i]);
}
printf("\n");
regfree(®);
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(®ex, 0, sizeof(regex));
err = re_compile_pattern(pattern, strlen(pattern), ®ex);
if (NULL == err)
{
struct re_registers regs;
memset(®s, 0, sizeof(regs));
re_set_registers(®ex, ®s, 0, NULL, NULL);
num = re_match(®ex, string, strlen(string), 0, ®s);
/* 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(®ex, ®s);
regfree(®ex);
}
return num;
}