正确写法是 preg_split('/(?= )/', $text, -1, PREG_SPLIT_NO_EMPTY),它用前瞻断言按换行符前位置分割,保留每行缩进和原始换行符。

preg_split 保留换行与缩进的关键写法
直接用 explode("
", $text) 会丢掉每行开头的空格和制表符,更不会保留原始换行符本身。真正要“按行切分且缩进不丢”,必须用 preg_split 配合零宽断言。
preg_split('/(? 是最简可行方案:它在每个 <code>后面“切一刀”,但不消耗换行符,所以每行开头的缩进原样保留在$lines[0]、$lines[1]等字符串里- Windows 换行是
,得覆盖两种情况:/(? —— 注意不能写成 <code>[ ],否则会被当成两个独立断点 - 如果文本含混合换行(比如从不同系统粘贴来的),先统一:
$text = str_replace(" ", " ", $text);再用/(?,比硬扛双换行更稳
提取 PHP 数组定义时,为什么不能只靠正则
像 array( ... ) 或 [...] 这类结构,嵌套、换行、注释、引号内含括号都会让纯正则崩溃。例如 ["a(b)c", "d]e"] 里的 ] 并非结束符,但正则无法判断上下文。
- 推荐用
token_get_all():它把 PHP 字符串解析成真实语法单元,能区分字符串字面量、注释、括号层级,比任何正则都可靠 - 若硬要用正则提取“看起来像数组”的片段,至少加
s修饰符让.匹配换行,并用非贪婪.*?限定范围,但依然可能截断在引号或注释里 - 常见翻车点:
preg_match('/\$vars*=s*(array(.*?)|[.*?]);/s', $code, $m)—— 遇到多层嵌套或单行注释// ]就失效
从 HTML 中提取内容时缩进丢失的根源
HTML 原始源码里的缩进,在浏览器渲染时被忽略,但如果你是从服务端原始 HTML 字符串中提取,缩进其实还在 DOM 文本节点里 —— 只是正则没注意保留它。
- 匹配标签内容时,用
([sS]*?)而不是(.*?),否则.默认不匹配换行,导致跨行内容截断 - 如果目标是提取
<div class="...">...</div>内部文本,且希望保留其中的换行和缩进,确保捕获组里包含空白:/<div[^>]*>([sS]*?)</div>/i - 注意:HTML 属性值里可能含双引号,若正则里用
"作定界符,属性值中的"必须转义,否则模式提前终止 —— 改用#定界更安全,如#<div class="box.*?">([sS]*?)</div>#i
大文本场景下缩进保留的性能取舍
对几 MB 的日志或模板文件,反复调用 preg_split 或复杂正则可能变慢,此时“保留缩进”和“快”得做权衡。
立即学习“PHP免费学习笔记(深入)”;
- 手动扫描更快:
$pos = 0; while (($n = strpos($text, " ", $pos)) !== false) { $line = substr($text, $pos, $n - $pos); $pos = $n + 1; /* 处理 $line */ }—— 不依赖 PCRE,内存友好 - 若后续还要分析缩进层级(比如 YAML 解析),别只数空格:用
strlen($line) - strlen(ltrim($line, " "))才能准确剔除制表符干扰 - 真正容易被忽略的是:缩进本身不是目的,目的是还原结构语义。比如 Markdown 列表缩进差 2 空格就降级,但正则只负责把原始空格传下去,层级逻辑得另外写栈式遍历



















