
本文详解如何使用支持多行匹配的正则表达式,从文本中可靠捕获 #%META 与 #%ENDS 之间的内容,并自动去除每行开头的 # 及空格,适用于配置解析、元数据抽取等场景。
本文详解如何使用支持多行匹配的正则表达式,从文本中可靠捕获 `#%meta` 与 `#%ends` 之间的内容,并自动去除每行开头的 `#` 及空格,适用于配置解析、元数据抽取等场景。
在处理结构化标记文本(如自定义元数据块)时,常见的挑战是:既要跨多行精确匹配起止标识符,又要干净地提取有效内容、剔除注释符号。原始尝试 (? 存在两个关键缺陷:一是未启用多行模式(<code>m),导致 ^/$ 锚点失效;二是使用 [\S\s] 虽可匹配换行,但无法阻止匹配跨越多个 #%META–#%ENDS 区块(贪婪/边界控制不足);三是完全未处理 # 前缀清理。
推荐采用以下高鲁棒性正则方案:
^#%META((?>\R(?!#%(?:META|ENDS)$).*)+)\R#%ENDS$
核心设计亮点:
-
^和$在m(multiline)模式下匹配每行首尾,确保精准定位独立区块; -
((?>\R(?!#%(?:META|ENDS)$).*)+)使用原子组(?>...)防止回溯灾难,内部通过负向先行断言(?!#%(?:META|ENDS)$)排除干扰行,严格限定匹配范围仅限于当前#%META到下一个#%ENDS之间; -
\R匹配任意 Unicode 换行符(\n,\r\n,\r等),兼容不同平台; - 捕获组
1直接获取目标内容(含换行),后续再统一清洗。
完整 PHP 示例:
一款AI工具,主要用于将PREQSTATION编码任务委派给Claude Code、Codex CLI或Gemini CLI,采用PTY安全执行(工作目录+后台+监控),适合需要提升相关任务效率的用户。
$re = '/^#%META((?>\R(?!#%(?:META|ENDS)$).*)+)\R#%ENDS$/m';
$str = '#%META
# date: 2022-08-27
# generated-by: Me
# id: 1
#%ENDS
#%BODY
....
#%ENDS
#%META
# date: 2022-08-27
# generated-by: Another Me
# id: 2
#%ENDS';
preg_match_all($re, $str, $matches);
$result = array_map(function($s) {
return preg_replace('/^#\h*/m', '', trim($s));
}, $matches[1]);
print_r($result);
输出:
Array ( [0] => date: 2022-08-27 generated-by: Me id: 1 [1] => date: 2022-08-27 generated-by: Another Me id: 2 )
注意事项:
- 务必启用
m(multiline)标志,否则^/$将只匹配整个字符串首尾; - 若目标环境不支持
\R(如旧版 JavaScript),可替换为(?:(?:\r\n?|\n)); - 原子组
(?>...)在 PCRE、Python 的regex库、.NET 中均支持,但 Python 标准re模块不支持,此时可用(?:(?!\n#%(?:META|ENDS)$)[^\n]*\n?)+替代; - 清洗步骤
preg_replace('/^#\h*/m', '', ...)中的\h匹配水平空白(空格、制表符),比\s更安全,避免误删行尾空白。
此方案兼顾准确性、可维护性与跨平台适应性,是解析多行标记区块的工业级实践。










