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

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


咨詢
建站咨詢
