php中preg_match默认不匹配换行,因.不匹配换行符;加s修饰符或用[\s\s]可实现跨行匹配,m修饰符仅影响^$锚定,与.无关。

PHP中preg_match默认不匹配换行?用s修饰符
PHP的PCRE引擎默认把.当作“除换行符外的任意字符”,所以preg_match('/a.b/', "a\nb")会失败。这不是bug,是设计行为。要让.能跨行匹配,必须加s(PCRE_DOTALL)修饰符。
实操建议:
-
preg_match('/a.*b/s', $text)——s让.覆盖换行符 - 别只写
/pattern/m:那是m(multiline)修饰符,只影响^和$的锚定行为,跟.无关 - 如果正则里没用
.,比如全靠[\s\S]或[\d\D],那s修饰符就完全没必要
不用s修饰符时怎么跨行匹配?用[\s\S]或[\d\D]
有些场景不能/不想改修饰符(比如正则由配置注入、或需兼容旧逻辑),这时可以用字符类绕过限制。
说明:[\s\S]表示“任意空白符或非空白符”,等价于“任意字符”,包括换行;[\d\D]同理,更短但语义稍弱。
实操建议:
-
preg_match('/a[\s\S]*b/', $text)安全、明确、无需修饰符 - 避免
.*混用s和m:比如/a.*b/sm虽然有效,但m在此无意义,易误导后续维护者 -
[\s\S]比.*略慢,但对常规文本长度(strpos或str_split替代正则
多行文本匹配失败的常见错误:没处理\r\n和编码问题
Windows换行符是\r\n,Linux/macOS是\n。如果源文本来自表单提交或文件读取,可能混用,而[\s\S]能覆盖\r,.+s也能,但前提是文本编码一致。
容易踩的坑:
- 从
file_get_contents()读取UTF-8文件后直接正则,但内容含BOM头——\xEF\xBB\xBF会被当成普通字符,导致开头匹配偏移 - 用
mb_ereg系列函数?别用,PHP 8.2+已废弃,且不支持s修饰符 - 正则里写
\n硬匹配?不可靠。换行符可能是\r\n,应交给.或[\s\S]自动处理
真实场景示例:提取HTML标签块中的多行内容
比如要从一段含换行的HTML中提取<div class="content">...</div>,里面可能有缩进、空行、JS注释。
推荐写法:
$pattern = '/<div>([\s\S]*?)/i'; preg_match($pattern, $html, $matches); $content = $matches[1] ?? ''; <p>说明:</p> <ul><li>用<code>[\s\S]*?</code>非贪婪匹配,防止跨到下一个<code></code> </li></ul> </div>
i修饰符忽略大小写,适配<div>等写法
<li>别用<code>/<div.>/is</div.>:万一中间有class="content"以外的属性(如id="xxx"),.*?可能跳过关键部分,漏匹配真正麻烦的从来不是修饰符本身,而是你拿到的文本到底含什么控制符、有没有BOM、是不是混合了不同来源的换行风格——先bin2hex(substr($text, 0, 4))看前几个字节,比反复调正则更快定位问题。











