字串替換是高頻操作,選擇正確函數能大幅影響效能。本文實測 strtr、str_replace、preg_replace 在不同模式下的速度差異,並從原始碼層面分析原因,提供選擇指南。
一、測試環境與方法
- PHP 7.4 / 8.1 典型環境
- 測試字串:長度約 200 字元,包含數字與字母
- 迴圈 10,000 次,取平均耗時
- 兩種替換模式:長鍵值(5 字元)與 短鍵值(1 字元 vs 10 字元)
二、測試代碼
<?php
$str = '111111110000000000000000000000000000000111000001000100010000010010000010010000010100000010';
$str = str_repeat($str, 1); // 長度 ~200
// 模式 1:長鍵值對應
$pattern1 = array('12345'=>'', '67891'=>'');
$pattern3 = '/12345|67891/';
$pattern5 = array('12345', '67891');
// 模式 2:短鍵 + 長鍵混合
$pattern2 = array('a'=>'', '1234567890'=>'');
$pattern4 = '/a|1234567890/';
$pattern6 = array('a', '1234567890');
$t = microtime(true);
for($i=0;$i<10000;$i++) strtr($str, $pattern1);
echo "strtr(long): " . (microtime(true)-$t) . "\n";
$t = microtime(true);
for($i=0;$i<10000;$i++) strtr($str, $pattern2);
echo "strtr(mixed): " . (microtime(true)-$t) . "\n";
$t = microtime(true);
for($i=0;$i<10000;$i++) preg_replace($pattern3, '', $str);
echo "preg_replace(long): " . (microtime(true)-$t) . "\n";
$t = microtime(true);
for($i=0;$i<10000;$i++) preg_replace($pattern4, '', $str);
echo "preg_replace(mixed): " . (microtime(true)-$t) . "\n";
$t = microtime(true);
for($i=0;$i<10000;$i++) str_replace($pattern5, '', $str);
echo "str_replace(long): " . (microtime(true)-$t) . "\n";
$t = microtime(true);
for($i=0;$i<10000;$i++) str_replace($pattern6, '', $str);
echo "str_replace(mixed): " . (microtime(true)-$t) . "\n";
三、典型測試結果(參考值,單位:秒)
| 函數 | 長鍵值模式 | 短長混合模式 | 相對速度 |
| str_replace | 0.18 | 0.18 | ⭐⭐⭐ 最快 |
| strtr | 0.22 | 0.48 | ⭐⭐ 受鍵長差異影響大 |
| preg_replace | 0.30 | 0.30 | ⭐ 穩定但較慢 |
四、原始碼層面分析
str_replace:逐鍵循環替換
- 遍歷
search 陣列每個元素,依序在 subject 中搜尋並替換。
- 無「最長匹配優先」邏輯,先出現的鍵優先替換。
- 例如:
str_replace(['ab','abc'], '1', 'abcd') → 1cd(先替換 ab)。
- 因無需建立查找表,短鍵值效能極佳。
strtr(陣列模式):建立查找表 + 最長匹配優先
- 掃描所有鍵,記錄
minlen、maxlen。
- 建立 HashTable 索引鍵。
- 掃描 subject 時,從
maxlen 遞減至 minlen 嘗試匹配,**最長優先**。
- 例如:
strtr('abcd', ['ab'=>'1','abc'=>'2']) → 2d(匹配 abc)。
- 鍵長差異大時,外層迴圈次數增加,效能下降明顯。
preg_replace:正則引擎編譯與執行
- 編譯正則模式(PCRE JIT 可加速)。
- 功能最強:支援邊界、分組、回溯引用等。
- 固定開銷較大,短字串替換不划算;複雜模式下不可替代。
五、關鍵差異示例:替換順序影響結果
<?php
$subject = 'abcd';
// str_replace:陣列順序決定優先權
echo str_replace(['ab', 'abc'], 'X', $subject); // Xcd
// strtr:最長匹配優先
echo strtr($subject, ['ab'=>'X', 'abc'=>'Y']); // Yd
// preg_replace:正則替代順序(左至右嘗試)
echo preg_replace('/ab|abc/', 'Z', $subject); // Zcd(先匹配 ab)
六、選擇指南
| 場景 | 推薦函數 | 理由 |
| 簡單字串替換、大量資料、追求極致速度 | str_replace | 無額外開銷,線性掃描 |
| 需「最長匹配優先」、字典式替換(如敏感詞過濾) | strtr | 語義正確,避免短詞搶先匹配 |
| 需正則表達式(邊界、分組、條件替換) | preg_replace | 功能完整,接受效能代價 |
| 單字元對應(如全形轉半形、編碼轉換) | strtr($str, $from, $to) | 三參數模式最快,但僅限單位元組 |
七、避坑提醒
- strtr 三參數模式
strtr($str, $from, $to) 僅對應單一位元組字元,多位元組(UTF-8 中文)會產生亂碼,**嚴禁用於多位元組字串**。
- str_replace 遞迴替換風險:若
replace 值包含 search 其他鍵,可能被二次替換。需求「單次通過」時請用 strtr 或 preg_replace_callback。
- 超大字串(>1MB):三者記憶體峰值相近,若極度敏感可考慮串流處理或分塊替換。