正则表达式
场景描述
编写程序匹配字符串中的 IP 地址:
cpp
// IP地址格式: xxx.xxx.xxx.xxx (0.0.0.0 ~ 255.255.255.255)
// 示例字符串: 123456765a192.168.31.1adasd123453
char str[16] = {0};
std::string GetIPAddress(char *ipaddress) {
char ip[16] = {0};
int count = 0;
for(int i = 0; ipaddress[i] != '\0'; i++) {
if(ipaddress[i] >= '0' && ipaddress[i] <= '9' && i == 3 && i == 7 && i == 11)
ip[count++] = ipaddress[i];
else if(ipaddress[i] == '.' && (i == 3 || i == 7 || i == 11))
ip[count++] = ipaddress[i];
}
}传统方法问题:匹配逻辑复杂,代码量大
正则表达式概念
- 描述字符串规则的表达式
- 用于模糊搜索(查询)
- IP地址规则:
- 由四段组成
- 每段由点分割
- 每段数据范围 0~255
0~255范围_0~255范围_0~255范围_0~255范围
字符分类
| 类型 | 描述 | 示例 |
|---|---|---|
| 普通字符 | 代表自身 | 1, 2, a |
| 元字符 | 有特殊含义(需转义) | *, ., ? |
元字符详解
匹配任意单个字符
.:匹配任意单个字符
匹配结果:a,b,c,1,2,+,{,},<...
字符组匹配
[]:匹配字符组中任意一个字符regex[123456789] // 匹配1~9任意数字 [123abc<] // 匹配1,2,3,a,b,c,<-:表示范围regex[1-9] // 匹配1~9数字 [a-z] // 匹配a~z字母
特定字符匹配
\w:匹配字母/数字/下划线
匹配结果:0~9,A~Z,a~z,_
匹配一个或多个字符
+:贪婪匹配(至少一个)regex\w+ // 匹配:1, 11, 111, a, aa, a1... 1+ // 匹配:1, 11, 111...
匹配零个或多个字符
*:惰性匹配(零个或多个)regex\w* // 匹配:空, 1, 11, a, aa... 1* // 匹配:空, 1, 11...
匹配零个或一个字符
?:零或一次匹配regexab? // 匹配:a, ab
匹配指定数量字符
| 模式 | 描述 | 示例 | 匹配结果 |
|---|---|---|---|
{n} | 精确匹配n个 | a{5} | aaaaa |
{min,} | 至少匹配min个 | a{5,} | aaaaa, aaaaaaa... |
{min,max} | 匹配min~max个 | a{2,3} | aa, aaa |
子模式
():将内部表达式视为整体regex(12+)+ // 匹配:12, 122, 122122...
IP地址正则表达式构建
逐步构建
- 基础匹配:regex
\d+\.\d+\.\d+\.\d - 限制每段长度:regex
\d{1,3}\\.\d{1,3}\\.\d{1,3}\\.\d{1,3} - 范围筛选:regex
[1217[0-9]{1,2}\\.[1217[0-9]{1,2}\\.[1217[0-9]{1,2}\\.[1217[0-9]{1,2} - 简化表达式:regex
([1217[0-9]{1,2}\\.){3}[1217[0-9]{1,2}
验证工具:https://hiregex.com/
注意:C++可能不支持某些表达式
C/C++正则表达式API
cpp
#include <regex.h>
// 编译正则表达式
int regcomp(regex_t *preg, const char *regex, int cflags);
// 执行正则匹配
int regexec(const regex_t *preg, const char *string,
size_t nmatch, regmatch_t pmatch[], int eflags);
// 错误解析
size_t regerror(int errcode, const regex_t *preg,
char *errbuf, size_t errbuf_size);
// 释放正则对象
void regfree(regex_t *preg);函数详解
regcomp()
cpp
/**
* @brief 编译正则表达式
* @param preg 存储编译后的正则表达式
* @param regex 原始正则表达式字符串
* @param cflags 编译标志:
* REG_EXTENDED - 扩展语法
* REG_ICASE - 忽略大小写
* REG_NOSUB - 不包含子模式
* @return 成功返回0,失败返回错误码
*/
int regcomp(regex_t *preg, const char *regex, int cflags);regexec()
cpp
/**
* @brief 执行正则匹配
* @param preg 编译后的正则表达式
* @param string 待匹配字符串(母串)
* @param nmatch 模式数量(总模式=1+子模式数)
* @param pmatch 匹配结果数组
* @param eflags 匹配标志(通常为0)
* @return 成功返回0,失败返回REG_NOMATCH
*/
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;regerror()
cpp
/**
* @brief 解析错误信息
* @param errcode 错误码
* @param preg 正则表达式
* @param errbuf 存储错误信息的缓冲区
* @param errbuf_size 缓冲区大小
* @return 错误信息长度
*/
size_t regerror(int errcode, const regex_t *preg,
char *errbuf, size_t errbuf_size);regfree()
cpp
/**
* @brief 释放正则表达式资源
* @param preg 正则表达式对象
*/
void regfree(regex_t *preg);IP地址匹配实现
cpp
#include <iostream>
#include <regex.h>
#define IPREGEX "[0-9][1,3]\\.[0-9][1,3]\\.[0-9][1,3]\\.([0-9][1,3])"
#define MAXSIZE 512
int main() {
int errcode = 0;
char errbuf[MAXSIZE] = {0};
const char *src_ptr =
"192.168.31.ladsadasdadasdsadasd45das35d4as53d12313a5192.163.32.2";
regex_t regex;
errcode = regcomp(®ex, IPREGEX, REG_EXTENDED);
if(errcode != 0) {
regerror(errcode, ®ex, errbuf, MAXSIZE);
std::cout << "正则表达式编译错误:" << errbuf << std::endl;
return -1;
}
regmatch_t pmatch[2];
int offset = 0;
do {
errcode = regexec(®ex, offset + src_ptr, 2, pmatch, 0);
if(errcode != REG_NOMATCH) {
std::cout << "结果下标:" << pmatch[0].rm_so << ":"
<< pmatch[0].rm_eo << std::endl;
for(int pos = offset + pmatch[0].rm_so;
pos < offset + pmatch[0].rm_eo; pos++) {
std::cout << src_ptr[pos];
}
std::cout << std::endl;
offset += pmatch[0].rm_eo;
}
} while(errcode != REG_NOMATCH);
regfree(®ex);
return 0;
}作业
编写匹配邮箱号的程序,要求支持以下后缀:
- 邮箱后缀:
.com/.cn/.net - 邮箱服务商:
@qq/@163/@126/@gmail
