1329 字
8 分钟
Linux 服务器高阶编程(4):POSIX 正则函数与 mmap 文件匹配
Linux 服务器高阶编程(4):POSIX 正则函数与 mmap 文件匹配
一、在 C 程序中使用正则
使用 POSIX 正则函数需要包含头文件:
#include <regex.h>regex_t:正则类型
regex_t reg;regcomp():编译正则表达式
把 regstr 转换为可供正则函数使用的类型,并保存到 reg 中
regcomp(®, regstr, 0);®:接收编译结果的regex_t变量地址regstr:字符串形式的正则表达式0:使用默认编译选项
regfree():释放正则类型
regcomp() 生成的正则类型内部会使用资源,使用完成后要释放,避免内存泄漏
regfree(®);regerror():取得正则错误信息
用来取得正则错误信息,并把错误文本保存到用户提供的字符缓冲区中
- 检查正则表达式的语法
- 在表达式异常时取得错误描述
- 便于定位复杂表达式的问题
regexec():执行匹配
regexec(®, data, regnum, match, 0);参数含义:
®:已经由regcomp()生成的正则类型data:数据源,也就是从中查找结果的目标数据regnum:表达式数量match:传出匹配位置的regmatch_t数组0:默认匹配选项
返回值:
- 返回
0:匹配成功 - 返回
REG_NOMATCH:没有匹配到结果
与 grep 一次遍历并显示多条结果不同,老师强调 regexec() 每调用一次只返回一条结果。若数据中存在多条记录,程序需要循环调用。
二、数据源可以来自哪里
正则函数匹配的是进程能够访问的数据,并不局限于某一种输入形式
- 文件数据:先
open()打开文件,再用read()把内容读入缓冲区 - 标准输入:用户运行程序后从
stdin输入的数据 - 程序参数:执行程序时通过命令行传入的数据
- 内存数据:程序已经保存在某段内存中的数据
main() 的两个参数
./app ad 20 8int main(int argc, char** argv)此时:
argc = 4argv[0] = "./app"argv[1] = "ad"argv[2] = "20"argv[3] = "8"程序路径 ./app 本身也算一个参数。只要进程能够访问这些字符串,就可以把它们作为正则匹配的数据源
三、表达式数量与父、子表达式
为什么需要子表达式
假设要从下面的文本中得到人名
<name>刘备</name>外层 <name>...</name> 是定位数据所必需的规则,但真正需要提取的是中间的“刘备”,因此要把关键内容写成子表达式
<name>\([^<]\+\?\)</name>这里:
- 整个表达式负责定位完整的
<name>标签 - 圆括号中的表达式负责捕获标签正文
[^<]限制正文不能跨入下一个标签
表达式数量的计算
表达式数量 = 子表达式数量 + 1多出来的 1 是整个父表达式本身
例如:
- 只捕获一个
<name>标签正文:父表达式 1 个、子表达式 1 个,regnum = 2。 - 同时捕获姓名与地址:父表达式 1 个、子表达式 2 个,
regnum = 3。
HTML 超链接示例
<a href="https://www.baidu.com">点击访问百度</a>若既要提取 href 中的地址,又要提取标签正文,就需要两个子表达式。加上完整的 <a>...</a> 父表达式,表达式总数为 3
四、regmatch_t 与匹配位置
regexec() 不会直接把目标文本复制到新字符串中,而是通过 regmatch_t 数组告诉程序“结果位于数据源的什么位置”
regmatch_t match[regnum];每个 regmatch_t 元素保存两个关键偏移
rm_so:匹配结果的起始位置rm_eo:匹配结果的末尾位置
数组各元素的含义
match[0]:父表达式,也就是完整匹配结果的位置match[1]:第一个子表达式的位置match[2]:第二个子表达式的位置- 后续元素依次对应后续子表达式
因此,数组长度等于表达式数量;每个元素又能保存起始与末尾两个位置
例如
snprintf(name, match[1].rm_eo - match[1].rm_so + 1, "%s", data + match[1].rm_so);理解这一行:
match[1].rm_so是姓名子表达式的起点。match[1].rm_eo - match[1].rm_so是姓名长度。data + match[1].rm_so把数据源指针移动到姓名起始位置。- 长度再加
1,为字符串末尾的\0留出位置。
五、用 mmap 快速取得文件内容
把整个小文件映射到进程内存中,随后直接把映射地址作为正则匹配的数据源
基本步骤:
open()打开文件lseek()移动到文件末尾并取得文件大小mmap()建立映射- 映射成功后,指针指向的内存就包含文件数据
- 文件描述符不再需要时调用
close() - 使用结束后调用
munmap()解除映射
示例
int fd = open("a.html", O_RDWR);int fsize = lseek(fd, 0, SEEK_END);
char* mmap_ptr = NULL;mmap_ptr = mmap(NULL, fsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);close(fd);参数含义:
NULL:由系统选择映射地址。fsize:映射长度等于文件大小。PROT_READ | PROT_WRITE:映射区可读、可写。MAP_PRIVATE:建立私有映射。fd:被映射文件的描述符。0:从文件起始位置开始映射。
循环匹配时会不断移动 mmap_ptr,因此要先保存原始映射地址,最后才能正确解除映射
char* copy_ptr = mmap_ptr;六、邮箱匹配练习
ludya^_^@163.com限定条件
- 用户名长度为 8~15 个字符
- 用户名由大小写字母、数字或下划线组成
- 企业名由大小写字母或数字组成
- 后缀只接受
.com或.cn - 必须从行首匹配到行尾,不能只截取一行中的局部内容
grep '^[a-zA-Z0-9_]\{8,15\}@[a-zA-Z0-9]\+\.\(com\|cn\)$' b.c 分享
如果这篇文章对你有帮助,欢迎分享给更多人!
Linux 服务器高阶编程(4):POSIX 正则函数与 mmap 文件匹配
https://joyanblog.com/posts/linux-server-advanced-programming-04-posix-regex-mmap/ 相关文章 智能推荐
1
Linux 服务器高阶编程(3):正则表达式基础与 grep 应用
Linux 服务器高阶编程 整理正则表达式的基本概念、grep 使用方式、常见元字符、手机号匹配示例和贪心匹配。
2
Linux 服务器高阶编程(1):GitHub 基础
Linux 服务器高阶编程 整理 GitHub 仓库、分支、项目查找、仓库页面、本地 Git 配置和 SSH 设备认证相关内容。
3
Linux 服务器高阶编程(2):Git 项目上传与 Markdown 基础
Linux 服务器高阶编程 整理 Git 远程仓库别名、项目上传、暂存与提交操作、开源项目下载,以及 Markdown 常用语法。
4
Linux 网络编程(26):Linux 文件 I/O 与 ext2 文件系统
Linux 网络编程 Linux 网络编程系列第 26 篇,整理阻塞与非阻塞 I/O、fcntl、ext2 文件系统布局、inode 寻址、目录与路径查找、硬链接与符号链接,以及 stat、access 和 chmod 等文件接口。
5
Linux 网络编程(24):Makefile——增量编译、变量与规则
Linux 网络编程 Linux 网络编程系列第 24 篇,整理 Makefile 的增量编译原理、基本规则、自定义变量与自动变量、模式规则、内置函数,以及编译、清理和安装等功能目标。
随机文章 随机推荐
1
Linux 网络编程(26):Linux 文件 I/O 与 ext2 文件系统
Linux 网络编程 2026-08-20
2
Linux 服务器高阶编程(1):GitHub 基础
Linux 服务器高阶编程 2026-09-13
3
Linux 网络编程(17):IM 聊天系统——数据库设计和创建、注册功能实现、登录功能实现
Linux 网络编程 2026-08-08
4
Linux 网络编程(10):数据库语法 1
Linux 网络编程 2026-07-29
5
Linux 网络编程(15):IM 聊天系统——网络功能实现:TCP 协议服务端
Linux 网络编程 2026-08-04





