PHP source code reading strtr, php source code strtr
Strtr
Converts specific characters in a string. However, this function can be used in many ways.
Echo strtr ('Hello world', 'hw ',' AB '); // aello borldecho strtr ('Hello world', 'hw', 'A '); // aello worldecho strtr ('Hello world', ['hello' => 'Hi']); // The third type is hi worldecho strtr ('Hello world ', ['H' => 'th', 'Hello' => 'Hi']); // type 4 hi world
Time Complexity
O (n), the worst is O (n * m)
Source code
The source code is analyzed one by one based on each situation.
The first and second types are also the most commonly used, but the second type only converts 'H' to 'A', and 'w' is not processed. The replacement of this method is subject to the shorter party. If one of the from and to is an empty string, the original string is directly returned.
RETURN_STR (php_strtr_ex (str, Z_STRVAL_P (from), to, MIN (Z_STRLEN_P (from), to_len); // from source code MIN (Z_STRLEN_P (from), to_len )) it can be seen that the two strings from and to are short, and the remaining strings are ignored. Therefore, it can be explained that 'W' is ignored in the second case. // likewise, 'B' in the following to will also be ignored strtr ('Hello world', 'h', 'AB'); // aello world
Next, let's take a look at how the php_strtr_ex method achieves character conversion. The source code is implemented using the hash table. The hash table maps each character of the from to the corresponding location.
Static zend_string * php_strtr_ex (zend_string * str, char * str_from, char * str_to, size_t trlen) {// The value of trlen is MIN (Z_STRLEN_P (from), to_len )) // create a hash table first and use php pseudo code to explain the hash table created in the first case // 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];} // traverse the string and find the converted character for (I = 0; I <ZSTR_LEN (str); I ++) {if (ZSTR_VAL (str) from the hash table) [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); // find the converted character ZSTR_VAL (new_str) [I] = xlat [(size_t) (unsigned char) ZSTR_VAL (str) from the hash table) [I]; break ;}}for (; I <ZSTR_LEN (str); I ++) {// find the converted character ZSTR_VAL (new_str) from the hash table) [I] = xlat [(size_t) (unsigned char) ZSTR_VAL (str) [I];}
The third and fourth types of from are arrays. If the from is an array, it is not a one-to-one conversion, but a String Conversion, converts the entire key string to a value string.
Third, the from array has only one key-value pair. The implementation idea is to search for the key (Replaced string) position in the master string based on the kmp algorithm, replace the value. The kmp efficiency is O (n), So if m replacement is performed in the string, the strtr efficiency will be O (n * m)
// Search all locations of the replaced string. e = s = ZSTR_VAL (new_str); end = ZSTR_VAL (haystack) + ZSTR_LEN (haystack ); // php_memnstr search all the positions of the string to be replaced and replace 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) ++ ;}
Fourth, replace multiple strings with arrays, which is the most efficient in various situations.
// Construct all replaced strings ZEND_HASH_FOREACH_STR_KEY (pats, str_key) {len = ZSTR_LEN (str_key ); // calculate the longest and shortest values of all replaced strings if (len> maxlen) {maxlen = len;} if (len <minlen) {minlen = len ;} // record the hash value of each key length value num_bitset [len/sizeof (zend_ulong)] | = Z_UL (1) <(len % sizeof (zend_ulong )); // record the hash value of the first character of each key. 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 (); // two hash tables are supported. The replaced string old_pos = pos = 0; while (pos <= slen-minlen) {key = str + pos; // If the hash table matches from the first character, it indicates that the string starting with key [0] may be replaced by 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;} // The key loops from maxlen to minlen The fourth 'hello' and 'hes' are used to match hello while (len> = minlen) first) {// If the length from the hash table matches the possible length of the string to be replaced, find the replaced key value pair zend_hash_str_find if (num_bitset [len/sizeof (zend_ulong)] & (Z_UL (1) from the from Array) <(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 ++ ;}
This situation is a bit complicated. The following php pseudocode translates the above C language code.
$ Bitset = array_fill (0,255, 0); // hash table with the first character $ num_bitset = array_fill (0,255, 0); // hash value of the key length value $ min_len = PHP_INT_MAX; $ max_len = 0; $ len = 0; // echo strtr ('Hello world', ['hes' => 'th ', 'Hello' => 'Hi']); $ pats = ['hes', '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); // For example, if we match hello, the first character is h, the length is 5 // The following two rows are in the while loop of the C language above. The two if statements are used to determine echo $ bitset [intdiv (ord ('H'), 8)]. & 1 <(ord ('H') % 8), PHP_EOL; echo $ num_bitset [intdiv (5, 8)] & 1 <(5% 8), PHP_EOL;
Link to this article: JC blog-PHP source code reading strtr
Original article, reprinted please indicate the source