explode() 在大文本中慢因全量扫描与内存复制;手动用 strpos()+substr() 可快2–5倍,支持提前退出、减少拷贝和中间数组。

为什么 explode() 在大文本里慢得明显
因为 explode() 默认做完整字符串扫描 + 内存复制:它会把整个文本读进内存,再按分隔符切出所有子串,最后一次性分配数组并拷贝数据。遇到几 MB 的日志或 CSV 内容,光是内存分配和复制就占掉大半时间,更别说分隔符频繁出现时生成大量小字符串带来的 zval 开销。
- 分隔符越短(比如
"\n"或"|"),explode()扫描次数越多,但实际切分逻辑不变 - 如果只想要前 N 行或某一段,
explode()仍会处理全文——纯属浪费 - PHP 8.0+ 虽优化了内部实现,但无法绕过「全量构建数组」这一根本行为
用 strpos() + substr() 手动遍历真能快多少
能快 2–5 倍,尤其在“只取前几段”或“边读边处理”的场景下。核心是避免构造中间数组、减少内存拷贝,并支持提前退出。
- 每次只找下一个分隔符位置,用
substr()截出当前段,处理完立刻丢弃引用 - 用
strpos($text, $delim, $offset)的第三个参数跳过已扫描区域,避免重复搜索 - 注意:
strpos()返回false时必须显式判断,不能用== false(会把位置 0 当失败)
示例片段:
$text = "a|b|c|d|e";
$delim = "|";
$offset = 0;
while (($pos = strpos($text, $delim, $offset)) !== false) {
$segment = substr($text, $offset, $pos - $offset);
// 处理 $segment
$offset = $pos + strlen($delim);
}
// 最后一段
if ($offset < strlen($text)) {
$segment = substr($text, $offset);
// 处理末段
}比手写 strpos 更省心的替代方案
如果不想维护边界逻辑,又不愿承受 explode() 的开销,strtok() 是被严重低估的选择——它基于 C 层状态机,无数组分配,且天然支持流式消费。
立即学习“PHP免费学习笔记(深入)”;
-
strtok($text, $delim)初始化,之后反复调用strtok($delim)获取下一段 - 返回
false表示结束,无需手动算偏移 - 不支持多字符分隔符(如
"\r\n"),单字符或简单集合(" \t\n")没问题 - 注意:
strtok()修改内部静态状态,不能在嵌套循环里混用多个 tokenizers
真正影响性能的隐藏因素
很多人测出来“没变快”,其实卡在 I/O 或编码上,不是分割算法本身的问题。
- 文件直接
file_get_contents()读入——大文件会爆内存;改用fopen()+fgets()流式读行,再对每行做轻量分割 - 文本含 BOM 或 UTF-8 多字节字符,
strpos()按字节匹配可能切在字符中间;确认编码,必要时用mb_strpos()(但会慢一点) - 反复调用分割函数却没复用结果,比如在循环里对同一文本不断
explode()——应缓存或改用引用传递
最常被忽略的一点:当分隔符位置有规律(如固定宽度、CSV 格式),正则 preg_split() 加 PREG_SPLIT_NO_EMPTY 可能反而更稳,但务必禁用 PREG_SPLIT_DELIM_CAPTURE,否则额外捕获组开销极大。











