php函数之子字符串替换 str_replace


Posted in PHP onMarch 23, 2011

str_replace  子字符串替换 [str_replace]
mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )
php函数str_replace: 返回一个字符串或者数组。该字符串或数组是将 subject 中全部的 search 都被 replace 替换之后的结果。

现在我们所能知道的一些这个函数的用法,如:str_replace("#", "-", "dizaz#7#final"),str_replace(array('#', '$'), "-", "dizaz#7$final") 等,就这些调用方式,php内部是如何实现的呢,鉴于[深入理解PHP内核],在这里小做分析。

测试代码:

<?php 
$object = "dizaz#7#final"; 
$res = str_replace("#", "-", $object); 
echo $res;

如上,先从字符“#”替换为字符“-”开始。

预备工作:

下载PHP源代码,http://www.php.net下载即可
打造自己的阅读代码的工具[本人使用VIM+CSCOPE] 另:Linux用户也推荐图形化查看源代码工具kscope [google之]
编译工具[gcc],调试工具[gdb],另:GDB图形化端口DDD也很不错,推荐
编译PHP源码,记得使用--enable-debug [当然也希望通过./configure --help 看看PHP提供的一些编译选项,会有很多收获的]
开始分析:

通过[深入理解PHP内核]阅读,我们不难发现其PHP提供标准函数所在目录为PHP-SOURCE-DIR/ext/standard目录下,由于是字符串函数,很容易我们就可以在此目录下找到str_replace函数实现的文件 string.c,接下来就围绕着这个文件进行分析。[当然用CScope很容易就可以锁定,用:cs find s str_replace]

查询得知其定义实现:

/* {{{ 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); } 
/* }}} */

现在需要查看函数php_str_replace_common函数
/* {{{ 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); 
} 
} 
/* }}} */

继续跟踪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) { //例子中只有”#“所以,执行这一步。 
php_char_to_str_ex(Z_STRVAL_PP(subject),//subject的值,也就是dizaz#7#final 
Z_STRLEN_PP(subject), //获取subject的长度 
Z_STRVAL_P(search)[0], //由于只有1个”#”,所以只需要第一个字符 
Z_STRVAL_P(replace), //所要替换成的字符,现在是“-” 
Z_STRLEN_P(replace), //目标字符的长度,现在为1 
result, //替换结果 
case_sensitivity, //大小写是否敏感,默认是1 
replace_count); //替换次数 
} 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); 
} 
} 
}

到现在为止,我们的目标最终锁定到了php_char_to_str_ex 函数,现在只需要分析这个函数就OK了。其实现为:
/* {{{ 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) { //现在case_sensitivity = 1 
char *p = str, *e = p + len; 

 //计算需要替换几次 
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; 
} 
//计算替换以后的长度,并且存储到result中。 
Z_STRLEN_P(result) = len + (char_count * (to_len - 1)); 
//申请内存,存放替换后的数据 
Z_STRVAL_P(result) = target = safe_emalloc(char_count, to_len, len + 1); 
//设定结果是一个字符串 
Z_TYPE_P(result) = IS_STRING; 
//target跟result的值都指向统一块内存,所以只需要处理target 
if (case_sensitivity) { 
char *p = str, *e = p + len, *s = str; 
while ((p = memchr(p, from, (e - p)))) { //判断在第几个字符出现# 
memcpy(target, s, (p - s)); //把#以前的数据拷贝给target 
target += p - s; 
memcpy(target, to, to_len); //把目标字符拷贝给target[当然此时的target是开始target+p-s的] 
target += to_len; 
p++; 
s = p; 
if (replace_count) { 
*replace_count += 1; //设定替换次数 
} 
} 
//如果后面还有,继续添加到target后,这样target所指向的内存块已经是替换好的数据了。 
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; 
} 
/* }}} */

如上注释,其就这样完成了对于字符到字符串的替换。至于其中怎么return,怎么一个详细的过程,需要再对PHP执行过程有个相对的了解。
当然可以用gdb下断点到php_char_to_str_ex函数,来了解其详细执行过程。
下一篇来做对于字符串替换成字符串的分析。
小结:
其结果是存在zval中
其对替换的实现比较巧妙,可以学习
需要继续查看源码,学习更多编写技巧以及设计技巧。
PHP 相关文章推荐
给多个地址发邮件的类
Oct 09 PHP
source.php查看源文件
Dec 09 PHP
phpMyadmin 用户权限中英对照
Apr 02 PHP
PHP解析html类库simple_html_dom的转码bug
May 22 PHP
为百度UE编辑器上传图片添加水印功能
Apr 16 PHP
如何利用http协议发布博客园博文评论
Aug 03 PHP
PHP.vs.JAVA
Apr 29 PHP
PHP操作Redis常用技巧总结
Apr 24 PHP
PHP依赖注入原理与用法分析
Aug 21 PHP
PHP htmlspecialchars_decode()函数用法讲解
Mar 01 PHP
YII框架页面缓存操作示例
Apr 29 PHP
Thinkphp 框架配置操作之配置加载与读取配置实例分析
May 15 PHP
php expects parameter 1 to be resource, array given 错误
Mar 23 #PHP
php去掉字符串的最后一个字符附substr()的用法
Mar 23 #PHP
PHPUnit PHP测试框架安装方法
Mar 23 #PHP
开启CURL扩展,让服务器支持PHP curl函数(远程采集)
Mar 19 #PHP
windows下开发并编译PHP扩展的方法
Mar 18 #PHP
WordPress判断用户是否登录的代码
Mar 17 #PHP
用php的ob_start来生成静态页面的方法分析
Mar 09 #PHP
You might like
php mysql Errcode: 28 终极解决方法
2009/07/01 PHP
用PHP写的基于Memcache的Queue实现代码
2011/11/27 PHP
php中\r \r\n \t的区别示例介绍
2014/02/08 PHP
PHP加Nginx实现动态裁剪图片方案
2014/03/10 PHP
CodeIgniter记录错误日志的方法全面总结
2016/05/17 PHP
利用PHP命令行模式采集股票趋势信息
2016/08/09 PHP
实例讲解PHP表单处理
2019/02/15 PHP
TP5框架实现一次选择多张图片并预览的方法示例
2020/04/04 PHP
JS中彻底删除JSON对象组成的数组中的元素
2020/09/22 PHP
清华大学出版的事半功倍系列 javascript全部源代码
2007/05/04 Javascript
javascript parseInt 大改造
2009/09/27 Javascript
jquery移动listbox的值原理及代码
2013/05/03 Javascript
JS截取字符串常用方法详细整理
2013/10/28 Javascript
JavaScript实现移动端滑动选择日期功能
2016/06/21 Javascript
AngularJS 入门教程之事件处理器详解
2016/08/19 Javascript
利用Angular.js限制textarea输入的字数
2016/10/20 Javascript
JS实现最简单的冒泡排序算法
2017/02/15 Javascript
jQuery 表单序列化实例代码
2017/06/11 jQuery
聊聊JS动画库 Velocity.js的使用
2018/03/13 Javascript
angular项目中bootstrap-datetimepicker时间插件的使用示例
2018/03/15 Javascript
JavaScript模板引擎应用场景及实现原理详解
2018/12/14 Javascript
支付宝小程序实现省市区三级联动
2020/06/21 Javascript
javascript实现时钟动画
2020/12/03 Javascript
Vue使用鼠标在Canvas上绘制矩形
2020/12/24 Vue.js
Python爬虫框架Scrapy安装使用步骤
2014/04/01 Python
Python中django学习心得
2017/12/06 Python
python3+PyQt5实现自定义流体混合窗口部件
2018/04/24 Python
Python实现Restful API的例子
2019/08/31 Python
浅谈spring boot 集成 log4j 解决与logback冲突的问题
2020/02/20 Python
浅谈Html5页面打开app的一些思考
2020/03/30 HTML / CSS
豪华床上用品 :Jennifer Adams
2019/09/15 全球购物
工作的心得体会
2013/12/31 职场文书
优秀班集体申报材料
2014/12/25 职场文书
2015年安全工作总结范文
2015/04/02 职场文书
体育部部长竞选稿
2015/11/21 职场文书
Python字符串对齐方法使用(ljust()、rjust()和center())
2021/04/26 Python