轉換字串中特定的字符,但是這個函數使用的方式多種。
echo strtr('hello world', 'hw', 'ab'); // 第一种 aello borldecho strtr('hello world', 'hw', 'a'); // 第二种 aello worldecho strtr('hello world', ['hello' => 'hi']); // 第三种 hi worldecho strtr('hello world', ['he' => 'th', 'hello' => 'hi']); // 第四种 hi world
時間複雜度
O(n),最差是O(n*m)
原始碼
以下根據每種情況逐一分析原始碼。
第一種、第二種,也是最常用的,但第二種,只有’h’轉換成’a’,’w’沒有被處理。這種方式的替換,會以短的一方為準。如果from和to其中一個是空字串,會直接傳回原字串。
RETURN_STR(php_strtr_ex(str, Z_STRVAL_P(from), to, MIN(Z_STRLEN_P(from), to_len)));// 从源码MIN(Z_STRLEN_P(from), to_len))可以看出来,以from、to两个字符串短的为准,剩余的会被忽略掉,所以可以解释第二种情况'w'被忽略掉 // 同理,以下to中的'b'也会被忽略掉strtr('hello world', 'h', 'ab'); // aello world
接著,我們主要看下php_strtr_ex方法,是怎麼實作字元轉換。源碼是使用hash表實現,hash表把from的每個字符,一一對應為to的相應位置的字符。
static zend_string *php_strtr_ex(zend_string *str, char *str_from, char *str_to, size_t trlen) {// trlen的值就是MIN(Z_STRLEN_P(from), to_len)) // 先构建一个hash表,用php伪代码来解释第一种情况构建好的hash表 // array('g'=>'g','h'=>'a','i'=>'i','w'=>'b')unsigned char xlat[256], j = 0;do { xlat[j] = j; } while (++j != 256);for (i = 0; i
第三種、第四種from是數組,如果from是數組,情況就不是一對一的字元轉換,是字符字串對字串的轉換了,把key整個字串轉換成value字串。
第三種,from陣列只有一對鍵值對,實作想法是,根據kmp演算法在主字串中搜尋key(被取代的字串)的位置,如果找到,就使用value替換掉。 kmp本身的效率是O(n),所以若字串內進行了m次替換,這種情況下strtr效率會是O(n*m)
// 搜索被替换的字符串的所有位置e = s = ZSTR_VAL(new_str);end = ZSTR_VAL(haystack) + ZSTR_LEN(haystack);// php_memnstr搜索 被替换的字符串 的所有位置,并替换掉for (p = ZSTR_VAL(haystack); (r = (char*)php_memnstr(p, needle, needle_len, end)); p = r + needle_len) { memcpy(e, p, r - p); e += r - p; memcpy(e, str, str_len); e += str_len; (*replace_count)++; }
第四種,透過陣列取代多個字串,這種是各種情況效率最差的
// 先构造所有 被替换的字符串ZEND_HASH_FOREACH_STR_KEY(pats, str_key) { len = ZSTR_LEN(str_key);// 计算所有 被替换的字符串 最长和最短值if (len > maxlen) { maxlen = len; }if (len slen - pos) { len = slen - pos; }// key从maxlen循环到minlen,所以,第四种'hello'和'he',最先匹配到hellowhile (len >= minlen) {// 如果从长度hash表里面匹配到被替换的字符串里可能的长度,就从from数组里面找到替换的键值对zend_hash_str_findif ((num_bitset[len / sizeof(zend_ulong)] & (Z_UL(1)
這種情況有點複雜,下面的php偽程式碼翻譯一下以上的C語言程式碼
$bitset = array_fill(0, 255, 0); // 首字符的hash表$num_bitset = array_fill(0, 255, 0); // key长度值的hash值$min_len = PHP_INT_MAX;$max_len = 0;$len = 0; // echo strtr('hello world', ['he' => 'th', 'hello' => 'hi']); $pats = ['he', 'hello']; foreach($pats as $v){$len = strlen($v);if($len > $max_len) {$max_len = $len; } if($len