Convertit des caractères spécifiques dans une chaîne, mais cette fonction peut être utilisée de plusieurs manières.
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
Complexité temporelle
O(n), le pire est O(n*m)
Code source
Le code source est analysé un à un en fonction de chaque situation.
Les premier et deuxième types sont également les plus couramment utilisés, mais dans le deuxième type, seul « h » est converti en « a » et « w » n'est pas traité. Dans cette méthode de substitution, la plus courte prévaudra. Si l'un des éléments from et to est une chaîne vide, la chaîne d'origine sera renvoyée directement.
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
Ensuite, nous examinerons principalement la méthode php_strtr_ex pour réaliser la conversion de caractères. Le code source est implémenté à l'aide d'une table de hachage. La table de hachage mappe chaque caractère de au caractère correspondant de 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 < trlen; i++) { xlat[(size_t)(unsigned char) str_from[i]] = str_to[i]; } // 接着遍历字符串,从hash表中找到转换的字符for (i = 0; i < ZSTR_LEN(str); i++) {if (ZSTR_VAL(str)[i] != xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]]) { new_str = zend_string_alloc(ZSTR_LEN(str), 0); memcpy(ZSTR_VAL(new_str), ZSTR_VAL(str), i);// 从hash表中找到转换的字符ZSTR_VAL(new_str)[i] = xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]];break; } }for (;i < ZSTR_LEN(str); i++) {// 从hash表中找到转换的字符ZSTR_VAL(new_str)[i] = xlat[(size_t)(unsigned char) ZSTR_VAL(str)[i]]; } }
Les troisième et quatrième types de from sont des tableaux. Si from est un tableau, la situation n'est pas un caractère un à un. conversion. Il s'agit d'une conversion chaîne à chaîne, convertissant la chaîne clé entière en chaîne de valeur.
Le troisième type, le tableau from n'a qu'une seule paire clé-valeur. L'idée d'implémentation est de rechercher la position de la clé (chaîne remplacée) dans la chaîne principale selon l'algorithme kmp. avec de la valeur. L'efficacité de kmp lui-même est O(n), donc si m fois sont remplacés dans la chaîne, dans ce cas l'efficacité de strtr sera 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)++; }
La quatrième méthode consiste à remplacer plusieurs chaînes par des tableaux. C'est la moins efficace dans diverses situations
// 先构造所有 被替换的字符串ZEND_HASH_FOREACH_STR_KEY(pats, str_key) { len = ZSTR_LEN(str_key);// 计算所有 被替换的字符串 最长和最短值if (len > maxlen) { maxlen = len; }if (len < minlen) { minlen = len; }// 记录每个key长度值的hash值num_bitset[len / sizeof(zend_ulong)] |= Z_UL(1) << (len % sizeof(zend_ulong));// 记录每个key首字符的hash值bitset[((unsigned char)ZSTR_VAL(str_key)[0]) / sizeof(zend_ulong)] |= Z_UL(1) << (((unsigned char)ZSTR_VAL(str_key)[0]) % sizeof(zend_ulong)); } ZEND_HASH_FOREACH_END();// 辅助两个hash表,替换的字符串old_pos = pos = 0;while (pos <= slen - minlen) {key = str + pos;// 如果从首字符的hash表匹配到,表示以key[0]字符开头的有可能是被替换的字符串if (bitset[((unsigned char)key[0]) / sizeof(zend_ulong)] & (Z_UL(1) << (((unsigned char)key[0]) % sizeof(zend_ulong)))) { len = maxlen;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) << (len % sizeof(zend_ulong))))) { entry = zend_hash_str_find(pats, key, len);if (entry != NULL) { zend_string *s = zval_get_string(entry); smart_str_appendl(&result, str + old_pos, pos - old_pos); smart_str_append(&result, s); old_pos = pos + len;pos = old_pos - 1; zend_string_release(s);break; } } len--; } }pos++; }
Cette situation est un peu compliquée, car. suit le pseudo-code PHP traduit le code du langage C ci-dessus
$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 < $min_len) {$min_len = $len; } $num_bitset[intdiv($len,8)] |= 1 << ($len%8);$bitset[intdiv(ord($v[0]),8)] |= 1 << (ord($v[0])%8); }// print_r(array_unique($num_bitset)); // print_r(array_unique($bitset)); // 例如我们匹配hello,首字符是h,长度5 // 以下两行就是以上C语言的while循环里面两个if判断echo $bitset[intdiv(ord('h'),8)] & 1 << (ord('h')%8),PHP_EOL;echo $num_bitset[intdiv(5,8)] & 1 << (5%8),PHP_EOL;