在日常開發中,字串匹配與替換是常見操作。過去我習慣使用 str_replace 或 preg_replace,但聽說 strtr 效能不錯,決定實測比較並深入 PHP 原始碼一探究竟。
測試環境:PHP 7.x,執行 1000 次迴圈,對 MD5 產生的字串進行替換。
$i = 0;
$t = microtime(true);
for(;$i<1000;$i++) {
$str = strtr(md5($i), $p2);
}
var_dump(microtime(true)-$t); // 0.085476875305176
$t = microtime(true);
for(;$i<2000;$i++) {
$str = preg_replace($p, '', md5($i));
}
var_dump(microtime(true)-$t); // 0.09863805770874
結果顯示,strtr 效率比 preg_replace 高約 15% 左右。
查看 PHP_FUNCTION(strtr) 實現,主要流程:
php_strtr_arrayphp_strtrPHPAPI char *php_strtr(char *str, int len, char *str_from, char *str_to, int trlen)
{
int i;
unsigned char xlat[256];
if ((trlen < 1) || (len < 1)) return str;
// 初始化對應表:xlat[i] = i
for (i = 0; i < 256; xlat[i] = i, i++);
// 建立字元映射:from[i] -> to[i]
for (i = 0; i < trlen; i++) {
xlat[(unsigned char) str_from[i]] = str_to[i];
}
// 逐字元替換
for (i = 0; i < len; i++) {
str[i] = xlat[(unsigned char) str[i]];
}
return str;
}
此演算法建立 256 字元的查找表,時間複雜度 O(n + m),對單字元替換極高效。例如 strtr('abcdaaabcd', 'ab', 'efd') 輸出 efcdeeefcd(注意:'a'→'e', 'b'→'f', 'd' 保持不變)。
陣列模式支援多字元鍵值替換,採用「最長匹配優先」策略:
// 關鍵邏輯片段
while (pos < slen) {
if ((pos + maxlen) > slen) maxlen = slen - pos;
found = 0;
memcpy(key, str+pos, maxlen);
for (len = maxlen; len >= minlen; len--) {
key[len] = 0;
if (zend_hash_find(&tmp_hash, key, len+1, (void**)&trans) == SUCCESS) {
// 找到匹配,附加替換值,pos += len
found = 1; break;
}
}
if (!found) { smart_str_appendc(&result, str[pos++]); }
}
此設計確保 array('a'=>'e', 'ab'=>'f') 對 "abcd" 會優先匹配 "ab" 替換為 "f",而非先替換 "a"。
php_strtr 使用查找表,極快,適合大量單字元映射。php_strtr_array 採用最長匹配 + Hash 查找,邏輯嚴謹但開銷較大,鍵長度差異大時效能下降。strtr(string, string, string) 三參數形式,非單位元組字元易產生亂碼。