preg_split默认保留空字符串,因连续分隔符间存在零长度子串;加PREG_SPLIT_NO_EMPTY可过滤;需注意中英文破折号Unicode差异,推荐用/[-–—―−]/u匹配并加/u修饰符。

preg_split 用破折号分割字符串时,为什么空字符串会进结果?
默认情况下 preg_split 不会自动过滤空项,比如 "a--b" 用 '/-/' 分割,结果是 ['a', '', 'b']。这不是 bug,是正则匹配的自然行为——两个连续破折号之间确实存在一个长度为 0 的子串。
解决方法很简单:加 PREG_SPLIT_NO_EMPTY 标志:
$parts = preg_split('/-/', $text, -1, PREG_SPLIT_NO_EMPTY);
- 不加该标志 → 保留所有分割结果,含空字符串
- 加了 → 自动跳过空项,更接近日常语义上的“分词”预期
- 注意:这个标志只影响空字符串,不处理前后空白,如需去空格得额外
array_map('trim', ...)
中文破折号、连接号、减号混用导致分割失败
常见误区是只写 '/-/',但实际文本中可能混着全角破折号(—)、en dash(–)、em dash(—)、甚至中文顿号或减号(−)。它们 Unicode 码位不同,正则不会匹配。
稳妥做法是显式列出常用变体:
立即学习“PHP免费学习笔记(深入)”;
$pattern = '/[-–—―−]/u'; // 支持 ASCII 减号、en dash、em dash、全角连接号、Unicode 减号
-
/u修饰符必须加,否则 UTF-8 中文破折号会匹配失败或乱码 - 如果确定全是 ASCII 环境(如日志字段),可简化为
'/-/',但上线前务必检查原始数据来源 - 用
mb_ereg_replace或str_replace预处理统一成一种再分割,有时比写复杂正则更稳
分割后要保留分隔符?用捕获组 + PREG_SPLIT_DELIM_CAPTURE
默认 preg_split 丢弃分隔符。如果需要像 "a-b-c" → ['a', '-', 'b', '-', 'c'] 这样保留破折号本身,得靠捕获组和标志配合:
$parts = preg_split('/(-)/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
- 括号把
-包成捕获组,PREG_SPLIT_DELIM_CAPTURE让它也进结果数组 - 仍建议加上
PREG_SPLIT_NO_EMPTY,避免因开头结尾是破折号产生空项 - 注意:如果用了多个不同分隔符(如
[-–—]),捕获组会把实际匹配到的那个字符原样返回,适合做后续类型判断
性能敏感场景下,别硬上 preg_split
如果只是简单按 ASCII 破折号切分,且确认输入无 Unicode 异常,explode('-', $text) 比 preg_split 快 3–5 倍,内存占用更低。
-
explode不支持正则,不能处理多种破折号变体,也不能保留分隔符 - 若需去空、trim、过滤空项,组合
array_filter(array_map('trim', explode('-', $text)))依然比正则快 - 真正要用
preg_split的典型场景只有两个:需要多字符/多形态分隔符,或必须保留分隔符本身
线上高频调用的接口里,先 mb_strpos($text, '–') 判断是否存在非 ASCII 破折号,再决定走 explode 还是 preg_split,是个实用的平衡点。











