PHP源码阅读笔记七:nl2br, ltrim, rtrim, trim函数

PHP源码阅读笔记:nl2br, ltrim, rtrim, trim函数

string nl2br ( string string )

Returns string with ‘
‘ inserted before all newlines.

在代码中有注释如下:

/* it is really faster to scan twice and allocate mem once insted scanning once
and constantly reallocing */

程序先计算需要替换的个数,然后一次性计算需要分配的内存大小。从而减少了每次替换都重新分配内存的开销。

由此可见PHP源码的作者的程序优化上下了不少功夫。

源码摘抄如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 
    str = Z_STRVAL_PP(zstr);    //    字符串开始位置
    end = str + Z_STRLEN_PP(zstr);     //    字符串结束地址
 
    /* it is really faster to scan twice and allocate mem once insted scanning once
       and constantly reallocing */
    while (str < end) {       //    计算需要替换的位置个数
        if (*str == '\r') {
            if (*(str+1) == '\n') {
                str++;
            }
            repl_cnt++;
        } else if (*str == '\n') {
            if (*(str+1) == '\r') {
                str++;
            }
            repl_cnt++;
        }
 
        str++;
    }
 
    if (repl_cnt == 0) {    //    如果没有可替换的字符串,直接返回
        RETURN_STRINGL(Z_STRVAL_PP(zstr), Z_STRLEN_PP(zstr), 1);
    }
 
 
    //    给新生成的字符串分配内存
    new_length = Z_STRLEN_PP(zstr) + repl_cnt * (sizeof("<br />") - 1);
    tmp = target = emalloc(new_length + 1);
 
    str = Z_STRVAL_PP(zstr);
 
    while (str < end) {
        switch (*str) {
            case '\r':    //    没有break,直接转下个case
            case '\n':
                *target++ = '<';
                *target++ = 'b';
                *target++ = 'r';
                *target++ = ' ';
                *target++ = '/';
                *target++ = '>';
 
                if ((*str == '\r' && *(str+1) == '\n') || (*str == '\n' && *(str+1) == '\r')) {
                    *target++ = *str++;
                }
                /* lack of a break; is intentional */
            default:
                *target++ = *str;
        }
 
        str++;
    }
 
    *target = '\0';    //    添加最后的结束字符
 
    RETURN_STRINGL(tmp, new_length, 0);    //    返回结果

ltrim — Strip whitespace (or other characters) from the beginning of a string

rtrim — Strip whitespace (or other characters) from the end of a string

trim — Strip whitespace (or other characters) from the beginning and end of a string

这三个函数都是调用static void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)

===》PHPAPI char *php_trim(char *c, int len, char *what, int what_len, zval *return_value, int mode TSRMLS_DC)

实现,依据不同的mode(ltrim => 1, rtrim => 2, trim => 3)实现。

对于第二个参数,指过滤的字符,在默认情况下是 空格 \n\r\t\v\0

在程序中可以看到过滤用的字符仅有char mask[256];即ASCII 码的256个值

在使用php_charmask(unsigned char *input, int len, char *mask TSRMLS_DC)函数创建过滤用的字符HASH数组

如果是1或3(程序实现使用的是 mode & 1),则过滤源字符串前面的字符,从头开始遍历每个字符串,直接hash判断是否是需要过滤的字符,直到第一个不是过滤字符的位置结束

如果是2或 3(程序实现使用的是 mode & 2),则过滤源字符串后面的字符,过程与前面类似。

PHP源码阅读笔记七:nl2br, ltrim, rtrim, trim函数》上有5条评论

  1. Jacob

    按源码来看,这个函数在处理诸如\r\n或\n\r时会出现在之后再加一个\r或\n的情况,以’浪费’之前申请的内存,使返回串保证是申请的长度+1,而我在使用这个函数之后发现,替换之后串的长度增加了18,而不是想象中的15,请解惑?

    回复
  2. Jacob

    啊,不好意思,我发现我错了,好久没用c,忘了字符串特性了,sizeof跟字符串时会有个的结尾吧,-1是为了去掉的长度,函数的实现就是在各个换行符之前加个啊。。

    回复
  3. Pingback引用通告: PHP源码分析文章整理 | 牛腩五花肉的博客-linux系统编程

发表评论

电子邮件地址不会被公开。 必填项已用*标注


*

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>