电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

解析strtr函数的效率问题


发布日期:2024/4/3
 

最近经常要对字符串进行匹配和替换操作之前一般使用str_replace或者preg_replace据说strtr的效率不错所以对比了一下

复制代码 代码如下:
$i = ;
$t = microtime(true);
for(;$i<;$i++)
{
$str = strtr(md($i) $p);
}
var_dump(microtime(true)$t); //
$t = microtime(true);
for(;$i<;$i++)
{
$str = preg_replace($p md($i));
}
var_dump(microtime(true)$t); //


结果显示strtr的效率比preg_replace高约%左右
趁着周末查看了strtr的php源码

复制代码 代码如下:
PHP_FUNCTION(strtr)
{
zval **str **from **to;
int ac = ZEND_NUM_ARGS();
//参数检查(zend_get_parameters_ex函数定义在zend_apic文件中)
if (ac < || ac > || zend_get_parameters_ex(ac &str &from &to) == FAILURE) {
WRONG_PARAM_COUNT;
}
//参数检查
if (ac == && Z_TYPE_PP(from) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC E_WARNING "The second argument is not an array");
RETURN_FALSE;
}
convert_to_string_ex(str);
/* shortcut for empty string */
//宏Z_STRLEN_PP定义在zend_operatorsh
if (Z_STRLEN_PP(str) == ) {
RETURN_EMPTY_STRING();
}
if (ac == ) {
php_strtr_array(return_value Z_STRVAL_PP(str) Z_STRLEN_PP(str) HASH_OF(*from));
} else {
convert_to_string_ex(from);
convert_to_string_ex(to);
ZVAL_STRINGL(return_value Z_STRVAL_PP(str) Z_STRLEN_PP(str) );
php_strtr(Z_STRVAL_P(return_value)
Z_STRLEN_P(return_value)
Z_STRVAL_PP(from)
Z_STRVAL_PP(to)
MIN(Z_STRLEN_PP(from)
Z_STRLEN_PP(to)));
}
}


先看看php_strtr函数

复制代码 代码如下:
//trlen是字符串str_from与str_to的长度的最小值
PHPAPI char *php_strtr(char *str int len char *str_from char *str_to int trlen)
{
int i;
unsigned char xlat[]; //
if ((trlen < ) || (len < )) {
return str;
}
//xlat的下标与值相等
for (i = ; i < ; xlat[i] = i i++);
//把from到to字符串的每一个字符对应起来例如from="ab"to="cd"则会产生这样的对应a=>c b=>d
for (i = ; i < trlen; i++) {
xlat[(unsigned char) str_from[i]] = str_to[i];
}
//替换(不过觉得这个函数的效率还有可以改进的地方因为如果需要替换的字符只是占整个字符串很少的部分这样就有大部分的赋值操作其实并没有什么意义这样的情况下感觉先判断再赋值感觉会高效一点有空测试一下)
for (i = ; i < len; i++) {
str[i] = xlat[(unsigned char) str[i]];
}
return str;
}


可见在处理strtr(abcdaaabcd ab efd)这样的操作时应该是很高效的
(注意这个操作输出efcdeeefcd)
再看看php_strtr_array

复制代码 代码如下:


static void php_strtr_array(zval *return_value char *str int slen HashTable *hash)
{
zval **entry;
char *string_key;
uint string_key_len;
zval **trans;
zval ctmp;
ulong num_key;
int minlen = *;
int maxlen = pos len found;
char *key;
HashPosition hpos;
smart_str result = {};
HashTable tmp_hash;
//把替换数组从hash复制到tmp_hash并记录下标字符串的最大和最小长度
zend_hash_init(&tmp_hash NULL NULL );
zend_hash_internal_pointer_reset_ex(hash &hpos);
while (zend_hash_get_current_data_ex(hash (void **)&entry &hpos) == SUCCESS) {
switch (zend_hash_get_current_key_ex(hash &string_key &string_key_len &num_key &hpos)) {
case HASH_KEY_IS_STRING:
len = string_key_len;
if (len < ) {
zend_hash_destroy(&tmp_hash);
RETURN_FALSE;
}
zend_hash_add(&tmp_hash string_key string_key_len entry sizeof(zval*) NULL);
if (len > maxlen) {
maxlen = len;
}
if (len < minlen) {
minlen = len;
}
break;
//下标如果是整形的话会转换成字符串类型例如array(=>aa)转换成array(=>aa)
case HASH_KEY_IS_LONG:
Z_TYPE(ctmp) = IS_LONG;
Z_LVAL(ctmp) = num_key;
convert_to_string(&ctmp);
len = Z_STRLEN(ctmp);
zend_hash_add(&tmp_hash Z_STRVAL(ctmp) len+ entry sizeof(zval*) NULL);
zval_dtor(&ctmp);
if (len > maxlen) {
maxlen = len;
}
if (len < minlen) {
minlen = len;
}
break;
}
zend_hash_move_forward_ex(hash &hpos);
}
key = emalloc(maxlen+);
pos = ;
//从字符串的第一个字符开始循环匹配pos记录当前查找的位置
while (pos < slen) {
//当前位置加上最大长度如果大于字符串长度则最大长度就需要改变
if ((pos + maxlen) > slen) {
maxlen = slen pos;
}
found = ;
memcpy(key str+pos maxlen);
//从最大长度开始匹配就是说对abcd若array(a=>eab=>f)则会先把ab替换为f而不是先把a换成e
for (len = maxlen; len >= minlen; len) {
key[len] = ;
//因为使用了hash表所以这样的效率还是挺高的
if (zend_hash_find(&tmp_hash key len+ (void**)&trans) == SUCCESS) {
char *tval;
int tlen;
zval tmp;
if (Z_TYPE_PP(trans) != IS_STRING) {
tmp = **trans;
zval_copy_ctor(&tmp);
convert_to_string(&tmp);
tval = Z_STRVAL(tmp);
tlen = Z_STRLEN(tmp);
} else {
tval = Z_STRVAL_PP(trans);
tlen = Z_STRLEN_PP(trans);
}
//加入结果
smart_str_appendl(&result tval tlen);
//向前跳跃
pos += len;
found = ;
if (Z_TYPE_PP(trans) != IS_STRING) {
zval_dtor(&tmp);
}
break;
}
}
if (! found) {
smart_str_appendc(&result str[pos++]);
}
}
efree(key);
zend_hash_destroy(&tmp_hash);
smart_str_(&result);
RETVAL_STRINGL(resultc resultlen );
}

上一篇:Unserialize与Autoload

下一篇:Smarty foreach控制循环次数的实现详解