• 技术文章 >后端开发 >php教程

    php函数之子字符串替换 str_replace_PHP教程

    2016-07-21 15:31:28原创615

    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内核],在这里小做分析。

    测试代码:

    复制代码 代码如下:

    $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中
    其对替换的实现比较巧妙,可以学习
    需要继续查看源码,学习更多编写技巧以及设计技巧。

    www.bkjia.comtruehttp://www.bkjia.com/PHPjc/323049.htmlTechArticlestr_replace — 子字符串替换 [ str_replace ] mixed str_replace ( mixed $search , mixed $replace , mixed $subject [ , int nbsp; subject 中全部的 search 都被 replace 替换...

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:php 函数 之子 字符串 替换 amp #65279 str replace str replace 字符串
    上一篇:php 数组排序 array_multisort与uasort的区别_PHP教程 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • 一文详解PHP实现职责链设计模式(附代码示例)• php实现通过JSON RPC与go通讯(附代码)• 浅析怎么使用PHP做异步爬取数据• PHP8.3要有新函数了!(json_validate函数说明)• 设计API接口时,要注意这些地方!
    1/1

    PHP中文网