Heim >Backend-Entwicklung >PHP-Problem >Wie wird die PHP-Trim-Funktion implementiert?

Wie wird die PHP-Trim-Funktion implementiert?

藏色散人
藏色散人Original
2022-01-28 09:42:002362Durchsuche

php trim函数的实现原理:1、定义一个len来存储字符串的长度;2、判断c中的字符是否在hashmask中存在;3、如果存在,将len减去一位;4、如果不存在则停止操作。

Wie wird die PHP-Trim-Funktion implementiert?

本文操作环境:Windows7系统、PHP7.1版、DELL G3电脑

php源码分析trim函数的实现

在实际开发中遇到关于 trim 函数的2个问题:
    1:使用trim函数不能去除2个以上的连续点号(.)
    2 : 使用trim函数去除字符串的问题
先说一下第一个问题。
下面的一段代码:
    php -r "echo trim('abcdcba...','...');"
我的本意是要将字符串abcdcba...最后三个点去掉,结果是报错。

PHP Warning:  trim(): Invalid '..'-range, no character to the left of '..' in Command line code on line 1
Warning: trim(): Invalid '..'-range, no character to the left of '..' in Command line code on line 1
PHP Warning:  trim(): Invalid '..'-range, no character to the right of '..' inCommand line code on line 1
Warning: trim(): Invalid '..'-range, no character to the right of '..' in Command line code on line 1

这个问题其实很好解释,因为 trim 函数本书可以范围操作,例如 如果trim函数的第二个参数 a..d,它就会把a b c d 都去掉。因为省略号的原因,所以trim函数的第二个参数不能用..开头或者结尾。

第二个问题:
再看一个例子:
php -r 'echo trim("abcdcba","abc")."\n";'
我的本意是将字符串abcdcba最前面的abc去掉保留dcba,但结果却是这样的:
d
也就是说他会把a b c分别去掉。这应该算是个坑吧。

通过对底层源代码的分析来说一下为什么会出现这2种情况。
trim函数的源代码师在php代码根目录开始的 ext/standard/string.c
函数的定义如下:

PHP_FUNCTION(trim)
{
    php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
}

可以看到,定义调用了另外的函数,函数体如下:

static void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)
{
    char *str;
    char *what = NULL;
    int str_len, what_len = 0;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRM\_CC, "s|s", &str, &str_len, &what, &what_len) == FAILURE) {
        return;
    } 
    php_trim(str, str_len, what, what_len, return_value, mode TSRMLS_CC);
}

zend_parse_parameters函数的作用就是接受参数,有兴趣的同学可以查阅相关资料。从代码可以看到,函数接受了2个字符串类型的参数,一个str,就是需要处理的字符串,第二个参数是what,用来表示需要去除的字符。
这个函数在最后用调用了另外一个函数,函数php_trim,函数体如下:

PHPAPI char *php_trim(char *c, int len, char *what, int what_len, zval *return_value, int mode TSRMLS_DC)
{
    register int i;
    int trimmed = 0;
    char mask[256];
    
        if(what) {
            php_charmask((unsigned char*)what, what_len, mask TSRMLS_CC);
        } else {
            php_charmask((unsigned char*)" \n\r\t\v\0", 6, mask TSRMLS_CC);
        }

        if (mode & 1) {
            for (i = 0; i = 0; i--) {
                if (mask[(unsigned char)c[i]]) {
                    len--;
                } else {
                    break;
                }
            }
        }

        if (return_value) {
            RETVAL_STRINGL(c, len, 1);
        } else {
            return estrndup(c, len);
        }
        return "";
}

这个函数就是php真正处理去除操作的结构。
刚开始就是定义了简单的变量,再下面对变量what有一个判断,来判断是否传递了要去除的字符。可以看到,根据是不是传递了what,函数传递给php_charmask函数的参数不一样,从这儿可以看出,如果trim没有传要去除的字符,默认情况是去除" \n\r\t\v\0"六个字符的,下面来看看php_charmask函数进行了哪些操作。

static inline int php\_charmask(unsigned char *input, int len, char *mask TSRMLS_DC)
{
    unsigned char *end;
    unsigned char c;
    int result = SUCCESS;
    memset(mask, 0, 256);
    for (end = input+len; input = c) { 
            memset(mask+c, 1, input[3] - c + 1);
            input+=3;
        } else if ((input+1 = input) { /\* there was no 'left' char \*/
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
                result = FAILURE;
                continue;
            }
            if (input+2 >= end) { /\* there is no 'right' char \*/
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
                result = FAILURE;
                continue;
            }
            if (input[-1] > input[2]) { /\* wrong order \*/
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
                result = FAILURE;
                continue;
            }
            /* FIXME: better error (a..b..c is the only left possibility?) */
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid '..'-range");
            result = FAILURE;
            continue;
        } else {
            mask[c]=1;
        }
    }    
    return result;
}

这个函数的作用主要是,创建要去除的字符的哈希对应关系,刚开始考虑了特殊情况像a..d这样的情况(从这儿也能看出来为什么trim函数不能处理...的情况)。后面就是建立hash结构的过程。最后的结果是一个数组,以要去除的字符是 abc 为例:

    mask['a'] = 1;
    mask['b'] = 1;
    mask['c'] = 1;

这样的hash结构,最后返回的就是这个 mask(实际没有返回,使用引用变量传值的方式做到数据的返回)
前面的都是准备工作,后面的就是真正处理去除操作了。
通过源代码可以看到,下面的操作先对mode这个变量做了判断,那么mode这个变量是干嘛的?答案就是用来处理 ltrim rtirm trim3个函数的。
下面师一段C语言代码:

#includeint main(){
     printf("%d\n",1&1);
     printf("%d\n",2&2);
     printf("%d\n",3&1);
     printf("%d\n",3&2);
     return 0;
}

这段代码的输出结果如下:

1
2
1
2

通过这个大家可以看出来,trim的底层是怎么处理的。先对mode 分别取模,再做相应的操作。
实际的去除操作就很简单了。
定义一个len来存储字符串的长度,c 是一个字符指针,刚开始从左边开始去除,判断<em>c</em>中的字符是否在hashmask中存在,如果存在,就将c 的指针向后移动一位,将len减去一位,如果发现*c的字符不存在于hashmask中,停止操作(可能和实际代码逻辑不不一致,但思想师一样的)。相关代码如下:

for (i = 0; i

左边操作完成以后,右边的操作比较简单,从*c最右边开始匹配,如果匹配到,就将len的长度减1,如果没有旧停止操作。相关的代码如下:

for (i = len - 1; i >= 0; i--) {
    if (mask[(unsigned char)c[i]]) {
        len--;
    } else {
        break;
    }
}

最后就是一个简单返回操,把c指针现在指向的位置以后的len个字符返回。实现返回的操作。整个过程完成。
相关代码如下:

if (return_value) {
   RETVAL_STRINGL(c, len, 1);
} else {
   return estrndup(c, len);
}

最后感叹一下:所有的事情最重要的还是你自己.

推荐学习:《PHP视频教程

Das obige ist der detaillierte Inhalt vonWie wird die PHP-Trim-Funktion implementiert?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn