推荐用 preg_match_all 匹配句子而非 preg_split 分割:/1+[。!?;.!?\;\u{2026}]+/u,可保留标点、避免缩写误切、支持中英文及省略号,且比分割更可控。。!?;.!?\; ↩
PHP 用正则按句子分割文本的常见错误
直接用 preg_split('/[。!?;]+/', $text) 看似简单,但会丢掉标点、切不断英文句号(如 “Mr. Smith”)、忽略省略号(…)、误切小数点和邮箱里的点。更麻烦的是中文引号内的句末标点(如「你好!」)会被漏掉,因为正则没覆盖全角+半角+嵌套场景。
- 中文句末标点必须包含全角:。!?;
- 英文句末标点需排除缩写和数字中的点(如
Dr.、3.14) - 要保留分隔符本身(否则无法还原原始格式)
- 引号、括号内的句末标点不应作为分割点(但 PHP 原生正则不支持平衡组,得靠规避策略)
推荐的正则模式:兼顾中英文与常见例外
用这个表达式:/(? 是理想但不可行的——\s+ 前置断言在 PHP 中不支持变长。实际可用的是带 PREG_SPLIT_DELIM_CAPTURE 的捕获式分割:
$pattern = '/([。!?;\.\!\?\;])(?=\s*[\'"\u{2018}\u{2019}\u{201C}\u{201D}]*\s*[A-Z\u{4E00}-\u{9FFF}])/u';
$sentences = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
但更稳的做法是「匹配句子」而非「分割」:preg_match_all('/[^。!?;.!\?\;]+[。!?;.!\?\;]+/', $text, $matches),再过滤空项。它天然保留标点,且避开缩写误判(只要确保句末标点后跟空格或换行或字母/汉字)。
-
preg_match_all比preg_split更可控,尤其处理混合中英文时 -
[^。!?;.!\?\;]+匹配非句末标点的字符,避免跨句吞掉中间的点 - 末尾的
[。!?;.!\?\;]+允许连续多个标点(如!!、???、……) -
/u修饰符必不可少,否则 \u{4E00} 类 Unicode 范围无效
处理省略号、引号和缩写的实用技巧
中文省略号是 …(U+2026),不是三个点,正则里得显式加上;英文引号内句末标点(如 “Hello!” she said.)容易被误切,可加后瞻限制:只在标点后有空格+大写字母或汉字时才视为句界。