新聞中心
本文操作環(huán)境:Windows7系統(tǒng)、php7.1版、DELL G3電腦

10年積累的成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站設(shè)計(jì)后付款的網(wǎng)站建設(shè)流程,更有紫陽免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
php源碼分析trim函數(shù)的實(shí)現(xiàn)
在實(shí)際開發(fā)中遇到關(guān)于 trim 函數(shù)的2個(gè)問題:
1:使用trim函數(shù)不能去除2個(gè)以上的連續(xù)點(diǎn)號(hào)(.)
2 : 使用trim函數(shù)去除字符串的問題
先說一下第一個(gè)問題。
下面的一段代碼:php -r "echo trim('abcdcba...','...');"
我的本意是要將字符串abcdcba...最后三個(gè)點(diǎn)去掉,結(jié)果是報(bào)錯(cuò)。
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
這個(gè)問題其實(shí)很好解釋,因?yàn)?trim 函數(shù)本書可以范圍操作,例如 如果trim函數(shù)的第二個(gè)參數(shù) a..d,它就會(huì)把a b c d 都去掉。因?yàn)槭÷蕴?hào)的原因,所以trim函數(shù)的第二個(gè)參數(shù)不能用..開頭或者結(jié)尾。
第二個(gè)問題:
再看一個(gè)例子:php -r 'echo trim("abcdcba","abc")."\n";'
我的本意是將字符串abcdcba最前面的abc去掉保留dcba,但結(jié)果卻是這樣的:d
也就是說他會(huì)把a b c分別去掉。這應(yīng)該算是個(gè)坑吧。
通過對(duì)底層源代碼的分析來說一下為什么會(huì)出現(xiàn)這2種情況。trim函數(shù)的源代碼師在php代碼根目錄開始的 ext/standard/string.c
函數(shù)的定義如下:
PHP_FUNCTION(trim)
{
php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
}
可以看到,定義調(diào)用了另外的函數(shù),函數(shù)體如下:
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函數(shù)的作用就是接受參數(shù),有興趣的同學(xué)可以查閱相關(guān)資料。從代碼可以看到,函數(shù)接受了2個(gè)字符串類型的參數(shù),一個(gè)str,就是需要處理的字符串,第二個(gè)參數(shù)是what,用來表示需要去除的字符。
這個(gè)函數(shù)在最后用調(diào)用了另外一個(gè)函數(shù),函數(shù)php_trim,函數(shù)體如下:
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 "";
}
這個(gè)函數(shù)就是php真正處理去除操作的結(jié)構(gòu)。
剛開始就是定義了簡(jiǎn)單的變量,再下面對(duì)變量what有一個(gè)判斷,來判斷是否傳遞了要去除的字符??梢钥吹?,根據(jù)是不是傳遞了what,函數(shù)傳遞給php_charmask函數(shù)的參數(shù)不一樣,從這兒可以看出,如果trim沒有傳要去除的字符,默認(rèn)情況是去除" \n\r\t\v\0"六個(gè)字符的,下面來看看php_charmask函數(shù)進(jìn)行了哪些操作。
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;
}
這個(gè)函數(shù)的作用主要是,創(chuàng)建要去除的字符的哈希對(duì)應(yīng)關(guān)系,剛開始考慮了特殊情況像a..d這樣的情況(從這兒也能看出來為什么trim函數(shù)不能處理...的情況)。后面就是建立hash結(jié)構(gòu)的過程。最后的結(jié)果是一個(gè)數(shù)組,以要去除的字符是 abc 為例:
mask['a'] = 1;
mask['b'] = 1;
mask['c'] = 1;
這樣的hash結(jié)構(gòu),最后返回的就是這個(gè) mask(實(shí)際沒有返回,使用引用變量傳值的方式做到數(shù)據(jù)的返回)
前面的都是準(zhǔn)備工作,后面的就是真正處理去除操作了。
通過源代碼可以看到,下面的操作先對(duì)mode這個(gè)變量做了判斷,那么mode這個(gè)變量是干嘛的?答案就是用來處理 ltrim rtirm trim3個(gè)函數(shù)的。
下面師一段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;
}
這段代碼的輸出結(jié)果如下:
1 2 1 2
通過這個(gè)大家可以看出來,trim的底層是怎么處理的。先對(duì)mode 分別取模,再做相應(yīng)的操作。
實(shí)際的去除操作就很簡(jiǎn)單了。
定義一個(gè)len來存儲(chǔ)字符串的長度,c 是一個(gè)字符指針,剛開始從左邊開始去除,判斷c中的字符是否在hashmask中存在,如果存在,就將c 的指針向后移動(dòng)一位,將len減去一位,如果發(fā)現(xiàn)*c的字符不存在于hashmask中,停止操作(可能和實(shí)際代碼邏輯不不一致,但思想師一樣的)。相關(guān)代碼如下:
for (i = 0; i
左邊操作完成以后,右邊的操作比較簡(jiǎn)單,從*c最右邊開始匹配,如果匹配到,就將len的長度減1,如果沒有舊停止操作。相關(guān)的代碼如下:
for (i = len - 1; i >= 0; i--) {
if (mask[(unsigned char)c[i]]) {
len--;
} else {
break;
}
}
最后就是一個(gè)簡(jiǎn)單返回操,把c指針現(xiàn)在指向的位置以后的len個(gè)字符返回。實(shí)現(xiàn)返回的操作。整個(gè)過程完成。
相關(guān)代碼如下:
if (return_value) {
RETVAL_STRINGL(c, len, 1);
} else {
return estrndup(c, len);
}
最后感嘆一下:所有的事情最重要的還是你自己.
新聞標(biāo)題:phptrim函數(shù)是怎么實(shí)現(xiàn)的
文章URL:http://www.dlmjj.cn/article/dhehsoc.html


咨詢
建站咨詢
