php函數(shù)之子字符串替換 str_replace
str_replace — 子字符串替換 [str_replace]
mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )
php函數(shù)str_replace: 返回一個(gè)字符串或者數(shù)組。該字符串或數(shù)組是將 subject 中全部的 search 都被 replace 替換之后的結(jié)果。
現(xiàn)在我們所能知道的一些這個(gè)函數(shù)的用法,如:str_replace("#", "-", "dizaz#7#final"),str_replace(array('#', '$'), "-", "dizaz#7$final") 等,就這些調(diào)用方式,php內(nèi)部是如何實(shí)現(xiàn)的呢,鑒于[深入理解PHP內(nèi)核],在這里小做分析。
測試代碼:
<?php
$object = "dizaz#7#final";
$res = str_replace("#", "-", $object);
echo $res;
如上,先從字符“#”替換為字符“-”開始。
預(yù)備工作:
下載PHP源代碼,http://www.php.net下載即可
打造自己的閱讀代碼的工具[本人使用VIM+CSCOPE] 另:Linux用戶也推薦圖形化查看源代碼工具kscope [google之]
編譯工具[gcc],調(diào)試工具[gdb],另:GDB圖形化端口DDD也很不錯(cuò),推薦
編譯PHP源碼,記得使用--enable-debug [當(dāng)然也希望通過./configure --help 看看PHP提供的一些編譯選項(xiàng),會(huì)有很多收獲的]
開始分析:
通過[深入理解PHP內(nèi)核]閱讀,我們不難發(fā)現(xiàn)其PHP提供標(biāo)準(zhǔn)函數(shù)所在目錄為PHP-SOURCE-DIR/ext/standard目錄下,由于是字符串函數(shù),很容易我們就可以在此目錄下找到str_replace函數(shù)實(shí)現(xiàn)的文件 string.c,接下來就圍繞著這個(gè)文件進(jìn)行分析。[當(dāng)然用CScope很容易就可以鎖定,用:cs find s str_replace]
查詢得知其定義實(shí)現(xiàn):
/* {{{ proto mixed str_replace(mixed search, mixed replace, mixed subject [, int &replace_count])
Replaces all occurrences of search in haystack with replace */
PHP_FUNCTION(str_replace)
{
php_str_replace_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
現(xiàn)在需要查看函數(shù)php_str_replace_common函數(shù)
/* {{{ php_str_replace_common
*/
static void php_str_replace_common(INTERNAL_FUNCTION_PARAMETERS, int case_sensitivity)
{
/**
* TODO
* typedef struct _zval_struct zval;
* typedef struct _zend_class_entry zend_class_entry
*
* struct _zval_struct {
* zvalue_value value;
* zend_uint refcount__gc;
* zend_uchar type;
* zend_uchar is_ref__gc;
* };
*
* typedef union _zvalue_value {
* long lval;
* double dval;
* struct {
* char *val;
* int len;
* } str;
* HashTable *ht;
* zend_object_value obj;
* } zvalue_value;
*
* typedef struct _zend_object {
* zend_class_entry *ce;
* HashTable *properties;
* HashTable *guards;
* } zend_object;
*
*/
zval **subject, **search, **replace, **subject_entry, **zcount = NULL;
zval *result;
char *string_key;
uint string_key_len;
ulong num_key;
int count = 0;
int argc = ZEND_NUM_ARGS();
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|Z", &search, &replace, &subject, &zcount) == FAILURE) {
return;
}
SEPARATE_ZVAL(search);
SEPARATE_ZVAL(replace);
SEPARATE_ZVAL(subject);
/* Make sure we're dealing with strings and do the replacement. */
if (Z_TYPE_PP(search) != IS_ARRAY) {
....//代碼省濾
} else { /* if subject is not an array */
php_str_replace_in_subject(*search, *replace, subject, return_value, case_sensitivity, (argc > 3) ? &count : NULL);
}
if (argc > 3) {
zval_dtor(*zcount);
ZVAL_LONG(*zcount, count);
}
}
/* }}} */
繼續(xù)跟蹤php_str_replace_in_subject
/* {{{ php_str_replace_in_subject
*/
static void php_str_replace_in_subject(zval *search, zval *replace, zval **subject, zval *result, int case_sensitivity, int *replace_count)
{
zval **search_entry,
**replace_entry = NULL,
temp_result;
char *replace_value = NULL;
int replace_len = 0;
/* Make sure we're dealing with strings. */
convert_to_string_ex(subject);
Z_TYPE_P(result) = IS_STRING;
if (Z_STRLEN_PP(subject) == 0) {
ZVAL_STRINGL(result, "", 0, 1);
return;
}
/* If search is an array */
if (Z_TYPE_P(search) == IS_ARRAY) {
...//不走這步
} else {
if (Z_STRLEN_P(search) == 1) { //例子中只有”#“所以,執(zhí)行這一步。
php_char_to_str_ex(Z_STRVAL_PP(subject),//subject的值,也就是dizaz#7#final
Z_STRLEN_PP(subject), //獲取subject的長度
Z_STRVAL_P(search)[0], //由于只有1個(gè)”#”,所以只需要第一個(gè)字符
Z_STRVAL_P(replace), //所要替換成的字符,現(xiàn)在是“-”
Z_STRLEN_P(replace), //目標(biāo)字符的長度,現(xiàn)在為1
result, //替換結(jié)果
case_sensitivity, //大小寫是否敏感,默認(rèn)是1
replace_count); //替換次數(shù)
} else if (Z_STRLEN_P(search) > 1) {
Z_STRVAL_P(result) = php_str_to_str_ex(Z_STRVAL_PP(subject), Z_STRLEN_PP(subject),
Z_STRVAL_P(search), Z_STRLEN_P(search),
Z_STRVAL_P(replace), Z_STRLEN_P(replace), &Z_STRLEN_P(result), case_sensitivity, replace_count);
} else {
MAKE_COPY_ZVAL(subject, result);
}
}
}
到現(xiàn)在為止,我們的目標(biāo)最終鎖定到了php_char_to_str_ex 函數(shù),現(xiàn)在只需要分析這個(gè)函數(shù)就OK了。其實(shí)現(xiàn)為:
/* {{{ php_char_to_str_ex
*/
PHPAPI int php_char_to_str_ex(char *str, uint len, char from, char *to, int to_len, zval *result, int case_sensitivity, int *replace_count)
{
int char_count = 0;
int replaced = 0;
char *source, *target, *tmp, *source_end=str+len, *tmp_end = NULL;
if (case_sensitivity) { //現(xiàn)在case_sensitivity = 1
char *p = str, *e = p + len;
//計(jì)算需要替換幾次
while ((p = memchr(p, from, (e - p)))) {
char_count++;
p++;
}
} else {
for (source = str; source < source_end; source++) {
if (tolower(*source) == tolower(from)) {
char_count++;
}
}
}
if (char_count == 0 && case_sensitivity) {
ZVAL_STRINGL(result, str, len, 1);
return 0;
}
//計(jì)算替換以后的長度,并且存儲(chǔ)到result中。
Z_STRLEN_P(result) = len + (char_count * (to_len - 1));
//申請(qǐng)內(nèi)存,存放替換后的數(shù)據(jù)
Z_STRVAL_P(result) = target = safe_emalloc(char_count, to_len, len + 1);
//設(shè)定結(jié)果是一個(gè)字符串
Z_TYPE_P(result) = IS_STRING;
//target跟result的值都指向統(tǒng)一塊內(nèi)存,所以只需要處理target
if (case_sensitivity) {
char *p = str, *e = p + len, *s = str;
while ((p = memchr(p, from, (e - p)))) { //判斷在第幾個(gè)字符出現(xiàn)#
memcpy(target, s, (p - s)); //把#以前的數(shù)據(jù)拷貝給target
target += p - s;
memcpy(target, to, to_len); //把目標(biāo)字符拷貝給target[當(dāng)然此時(shí)的target是開始target+p-s的]
target += to_len;
p++;
s = p;
if (replace_count) {
*replace_count += 1; //設(shè)定替換次數(shù)
}
}
//如果后面還有,繼續(xù)添加到target后,這樣target所指向的內(nèi)存塊已經(jīng)是替換好的數(shù)據(jù)了。
if (s < e) {
memcpy(target, s, (e - s));
target += e - s;
}
} else {
for (source = str; source < source_end; source++) {
if (tolower(*source) == tolower(from)) {
replaced = 1;
if (replace_count) {
*replace_count += 1;
}
for (tmp = to, tmp_end = tmp+to_len; tmp < tmp_end; tmp++) {
*target = *tmp;
target++;
}
} else {
*target = *source;
target++;
}
}
}
*target = 0;
return replaced;
}
/* }}} */
如上注釋,其就這樣完成了對(duì)于字符到字符串的替換。至于其中怎么return,怎么一個(gè)詳細(xì)的過程,需要再對(duì)PHP執(zhí)行過程有個(gè)相對(duì)的了解。
當(dāng)然可以用gdb下斷點(diǎn)到php_char_to_str_ex函數(shù),來了解其詳細(xì)執(zhí)行過程。
下一篇來做對(duì)于字符串替換成字符串的分析。
小結(jié):
其結(jié)果是存在zval中
其對(duì)替換的實(shí)現(xiàn)比較巧妙,可以學(xué)習(xí)
需要繼續(xù)查看源碼,學(xué)習(xí)更多編寫技巧以及設(shè)計(jì)技巧。
- php獲取字符串前幾位的實(shí)例(substr返回字符串的子串用法)
- PHP實(shí)現(xiàn)求兩個(gè)字符串最長公共子串的方法示例
- PHP字符串中插入子字符串方法總結(jié)
- php實(shí)現(xiàn)指定字符串中查找子字符串的方法
- php根據(jù)指定位置和長度獲得子字符串的方法
- PHP中substr_count()函數(shù)獲取子字符串出現(xiàn)次數(shù)的方法
- PHP中比較兩個(gè)字符串找出第一個(gè)不同字符位置例子
- php刪除字符串末尾子字符,刪除開始字符,刪除兩端字符(實(shí)現(xiàn)代碼)
- php實(shí)現(xiàn)子字符串位置相互對(duì)調(diào)互換的方法
- php使用strpos判斷字符串中數(shù)字類型子字符串出錯(cuò)的解決方法
- php中有關(guān)字符串的4個(gè)函數(shù)substr、strrchr、strstr、ereg介紹和使用例子
- PHP字符串中抽取子串操作實(shí)例分析
相關(guān)文章
PHP PDOStatement:bindParam插入數(shù)據(jù)錯(cuò)誤問題分析
PHP PDOStatement:bindParam插入數(shù)據(jù)錯(cuò)誤問題分析,開發(fā)中一定要注意2013-11-11PHP實(shí)現(xiàn)基于PDO擴(kuò)展連接PostgreSQL對(duì)象關(guān)系數(shù)據(jù)庫示例
這篇文章主要介紹了PHP實(shí)現(xiàn)基于PDO擴(kuò)展連接PostgreSQL對(duì)象關(guān)系數(shù)據(jù)庫,結(jié)合實(shí)例形式分析了php使用pdo連接PostgreSQL并執(zhí)行簡單sql語句的相關(guān)操作技巧,需要的朋友可以參考下2018-03-03PHP數(shù)據(jù)的提交與過濾基本操作實(shí)例詳解
這篇文章主要介紹了PHP數(shù)據(jù)的提交與過濾基本操作,簡要說明了php數(shù)據(jù)過濾的基本方法并結(jié)合實(shí)例形式分析了php針對(duì)各種常見類型數(shù)據(jù)的過濾操作使用技巧,需要的朋友可以參考下2016-11-11深入php define()函數(shù)以及defined()函數(shù)的用法詳解
本篇文章是對(duì)php中的define()函數(shù)以及defined()函數(shù)的用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06PHP自定義函數(shù)獲取搜索引擎來源關(guān)鍵字的方法
這篇文章主要介紹了PHP自定義函數(shù)獲取搜索引擎來源關(guān)鍵字的方法,涉及php針對(duì)來路頁面URL的分析與判斷技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07PHP實(shí)現(xiàn)字符串翻轉(zhuǎn)功能的方法【遞歸與循環(huán)算法】
這篇文章主要介紹了PHP實(shí)現(xiàn)字符串翻轉(zhuǎn)功能的方法,結(jié)合實(shí)例形式對(duì)比分析了php使用遞歸與循環(huán)算法實(shí)現(xiàn)字符串反轉(zhuǎn)功能的相關(guān)操作技巧,需要的朋友可以參考下2017-11-11php中使用PHPExcel讀寫excel(xls)文件的方法
這篇文章主要介紹了php中使用PHPExcel讀寫excel(xls)文件的方法,phpExcel是常用的用于操作Excel的PHP類庫,應(yīng)用非常廣泛。需要的朋友可以參考下2014-09-09