Skip to content

进阶实战 05 · 正则表达式 std.regex:批量文本提取器 ​

《LingBuilder 进阶实战系列》第 5 集。日志、网页、聊天记录——文本处理的第一步是「把想要的东西抓出来」。本集用正则表达式模块(std.regex)做一个批量文本提取器:粘贴一段文本,点一下按钮,按行提取所有邮箱和链接。你还会学到程序员处理乱文本的标准姿势——「宽松解码 + 正则」形态校验范式。全程约 30 分钟。

前置要求:学完零基础入门系列与进阶前 4 集(特别是第 1 集的数组和第 3 集的「先声明再赋值」)。本集源码包见文末。

这一集你将学会 ​

  • 正则表达式模块的五条命令:正则_完全匹配、正则_是否包含、正则_取首个、正则_替换、正则_匹配数量
  • 用 #换行符 内置常量拼出多行文本
  • 用 文本_分割 + 计次循环首 逐行扫描、批量提取
  • 两种正则的写法:邮箱 [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+、链接 https?://[a-zA-Z0-9./_-]+
  • 「宽松解码 + 正则」形态校验范式:判断一段字节「像不像某种内容」的标准姿势

1. 三十秒理解:正则是什么 ​

正则表达式就是「文本的搜索公式」:用一串符号描述你要找的内容长什么样。比如:

  • [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+ —— 「一串字母数字点号,跟一个 @,再跟一串字母数字点号」= 邮箱的样子
  • https?://[a-zA-Z0-9./_-]+ —— 「http 或 https 开头,跟冒号斜杠,再跟一串地址字符」= 链接的样子

LingBuilder 的正则模块只有 5 条命令,本集用到其中两条:正则_是否包含(判断这行有没有)和 正则_取首个(取出第一个匹配)。

形态校验范式:判断一段字节「像不像某种内容」(验证码、账号、特定协议包),标准姿势是先用宽松解码(GB18030)转成文本,再用正则匹配特征——不要写逐字节状态机硬怼。

2. 新建项目与启用模块 ​

新建窗口项目:名称 批量文本提取器,模板「Windows 界面设计」,创建位置留空:

新建项目

启用「正则表达式模块」(22 条命令)。文本_分割、数组_取成员 等命令分别属于文本处理和数组操作模块,未启用的话一并启用。

3. 设计界面 ​

控件名称显示内容左距顶距宽度高度
标签说明标签把日志粘贴到下面,点按钮按行提取邮箱 / 链接303056024
编辑框原文框(空,勾选多行)3080560150
按钮邮箱按钮提取邮箱3024516030
按钮链接按钮提取链接21024516030
按钮清空按钮清空结果39024510030
列表框结果列表—30295560120
标签状态标签就绪。示例日志已载入,直接点按钮体验3042556024

窗口宽 620、高 500。原文框记得勾选「多行」,否则只能粘贴一行。绑 4 个按钮事件(被单击)+ 窗口「创建完毕」。

设计器全景

4. 写代码 ①:载入五行示例日志 ​

lcpp
事件 _MainWindow_创建完毕()
    局部 文本型 示例 = "联系张三:zhangsan@example.com"
    示例 = 示例 + #换行符 + "官网 https://lingbuilder.com 欢迎访问"
    示例 = 示例 + #换行符 + "这行没有联系方式"
    示例 = 示例 + #换行符 + "客服 support@lingbuilder.com,备用 admin@test.cn"
    示例 = 示例 + #换行符 + "文档在 https://docs.lingbuilder.com/guide"
    控件_设置文本(原文框, 示例)
    控件_设置文本(状态标签, "示例日志已载入,直接点按钮体验")
结束

载入示例

#换行符 是语言内置常量(文本型,回车+换行两个字符),开箱可用不需要声明。五行示例覆盖三种情况:有邮箱的行、有链接的行、什么都没有的行——正好检验提取器是否准确。

5. 写代码 ②:核心提取子程序 ​

两个按钮共用一个子程序,只是表达式不同:

lcpp
事件 _邮箱按钮_被单击()
    提取Pattern("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+")
结束

事件 _链接按钮_被单击()
    提取Pattern("https?://[a-zA-Z0-9./_-]+")
结束

事件 _清空按钮_被单击()
    控件_清空项目(结果列表)
    控件_设置文本(状态标签, "结果已清空")
结束

空 提取Pattern(文本型 表达式)
    控件_清空项目(结果列表)
    局部 文本型 全文 = 控件_取文本(原文框)
    局部 文本型 行[]
    文本_分割(全文, "\r\n", 行, 真)
    局部 整数型 命中数 = 0
    局部 整数型 序号 = 0
    局部 文本型 本行 = ""
    局部 文本型 命中 = ""
    计次循环首 (数组_取成员数(行), 序号)
        本行 = 数组_取成员(行, 序号 - 1)
        如果 (正则_是否包含(本行, 表达式))
            命中 = 正则_取首个(本行, 表达式)
            命中数 = 命中数 + 1
            控件_添加项目(结果列表, "第" + 到文本(序号) + "行:" + 命中)
        如果结束
    计次循环尾 ()
    控件_设置文本(状态标签, "提取完成:命中 " + 到文本(命中数) + " 行(每行取首个匹配)")
结束

提取子程序

拆解:

  • 文本_分割(全文, "\r\n", 行, 真):按回车换行把全文切成行数组。分隔符这里要写 "\r\n" 字面量,不要写 #换行符——#换行符 是 wstring 常量,拼接没问题,但传给「分隔符」这类指针参数位置会报 C2664 类型错(本集真机踩过并修好)。
  • 计次循环首 (数组_取成员数(行), 序号):有几行就循环几次;序号 - 1 是因为循环序号从 1 起,而数组下标从 0 起。
  • 正则_是否包含 先判断这行有没有匹配,有才 正则_取首个 取出来——避免对无匹配的行取到空文本。
  • 命中行以「第N行:内容」格式进结果列表,一眼能对应回原文。

循环体涉及的两个提取命令细节:正则_是否包含(文本, 表达式) 返回逻辑型,正则_取首个(文本, 表达式) 返回首个匹配文本(无匹配返回空)——两者都要求表达式合法,非法时取首个返回空文本、是否包含返回假,不会崩溃。

计次循环与提取

6. 编译与运行 ​

点「编译当前项目」,先看错误列表为 (0)、输出窗口「编译成功。」,然后点「运行」。程序启动时示例日志已载入原文框。点「提取邮箱」:

提取邮箱

第 1 行和第 4 行命中(每行取首个匹配,第 4 行有两个邮箱只报第一个——这正是 取首个 的语义)。点「提取链接」:

提取链接

第 2 行、第 5 行的链接被抓出来。试试删掉某行再提取,命中数会跟着变——提取逻辑完全由数据驱动。

7. 常见问题急救表 ​

症状原因解法
编译报 C2664 无法将 wstring 转换为 wchar_t*把 #换行符 常量传给了指针参数位置分隔符等参数位置改用 "\r\n" 字面量
永远只提取到第一个匹配正则_取首个 的语义就是首个按行分割后逐行提取(本集做法),或每行多次截取
提取邮箱把句尾句号带上表达式结尾字符类没排除句号用 [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} 更严谨
正则合法但匹配不到目标文本是 GBK 乱码宽松解码(GB18030)转文本再匹配
表达式非法返回空文本括号/方括号不配对先用 正则_是否包含 验证表达式可用性

本集小结 ​

  • 正则模块五条命令,本集用 正则_是否包含 + 正则_取首个 完成按行批量提取。
  • #换行符 拼接多行文本;作为分隔符参数时改用 "\r\n" 字面量。
  • 「宽松解码 + 正则」是处理来历不明文本的标准姿势。
  • 「先声明、再赋值」在循环体里同样适用(本行、命中 都是这么写的)。

下一集预告:《压缩与打包 archive》——把一堆文件一键打成 zip,学会 ZIP 命令族与内存压缩族,做一个文件夹一键压缩/解压工具。

回系列总览