在PHP里 有两个计算 字符串个数的函数

一个是 strlen,一个是mb_strlen;
先来看看帮助手册的定义

  • strlen

strlen — 获取字符串长度
int strlen ( string $string )
返回给定的字符串 string 的长度。

  • mb_strlen

int mb_strlen ( string $str [, string $encoding ] )

返回给定的字符串 string 的长度。

encoding参数为字符编码。如果省略,则使用内部字符编码。

 这么看 除了mb_strlen可以传递一个 字符编码好像没有其他区别

写一个PHP程序来看看

 

 
    
  1. $a="我是s斯t人"
  2. echo strlen($a); 
  3. echo "
    "
  4. echo mb_strlen($a,'utf8'); 

 

输出结果
14
6
很明显 strlen对于中文来讲,每个汉字占三个字节,$a的字节数就是14,说明 strlen计算的是字符串所占的字节数
在mb_strlen计算时,如果encoding设置为utf8,那么,中文将占一个字节 那么$a就是6

需要注意的是 mb_strlen并不是PHP内置函数,需要修改php.ini,开启 php_mbstring
好了..
我们下面就深入到PHP内核源码去看看两个函数的不同.
先看mb_strlen的定义

 
    
  1. PHP_FUNCTION(mb_strlen) 
  2.         int n; 
  3.         mbfl_string string; 
  4.         char *enc_name = NULL;        int enc_name_len; 
  5.   
  6.         mbfl_string_init(&string); 
  7.         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", (char **)&string.val, &string.len, &enc_name, &enc_name_len) == FAILURE) { 
  8.                 RETURN_FALSE; 
  9.         }     
  10.   
  11.         string.no_language = MBSTRG(language); 
  12.         if (enc_name == NULL) {                string.no_encoding = MBSTRG(current_internal_encoding); 
  13.         } else { 
  14.                 string.no_encoding = mbfl_name2no_encoding(enc_name); 
  15.                 if (string.no_encoding == mbfl_no_encoding_invalid) { 
  16.                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown encoding \"%s\"", enc_name);                        RETURN_FALSE; 
  17.                 } 
  18.         }             
  19.   
  20.         n = mbfl_strlen(&string); 
  21.         if (n >= 0) { 
  22.                 RETVAL_LONG(n); 
  23.         } else { 
  24.                 RETVAL_FALSE; 
  25.         } 

 

mbfl_string 是一个结构体

 

 
    
  1. typedef struct _mbfl_string { 
  2.         enum mbfl_no_language no_language; 
  3.         enum mbfl_no_encoding no_encoding; 
  4.         unsigned char *val;        unsigned int len; 
  5. } mbfl_string; 

看定义就知道,这两个保存了当前字符串的语言,编码,保存的值和长度
mbfl_string_init(&string);
这个函数是初始化string;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “s|s”, (char **)&string.val, &string.len, &enc_name, &enc_name_len) == FAILURE) {
RETURN_FALSE;
}
这里接收传递过来的两个参数,也就是前面看到的 string和encoding,
enc_name保存编码,enc_name_len保存编码名字的长度
string.no_language = MBSTRG(language);
展开后
#define MBSTRG(v) (mbstring_globals.v)
这里将mbstring的language赋给string的no_language;

 

 
    
  1. if (enc_name == NULL) { //如果没有手动设置编码,那么就使用PHP内置的编码 这个时候就与strlen相同 
  2.          string.no_encoding = MBSTRG(current_internal_encoding); 
  3.  }else { 
  4.                //检查是否是有效的编码 
  5.                string.no_encoding = mbfl_name2no_encoding(enc_name); 
  6.                //如果传递来的编码无效 抛出异常 
  7.                 if (string.no_encoding == mbfl_no_encoding_invalid) { 
  8.                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown encoding \"%s\"", enc_name); 
  9.                         RETURN_FALSE; 
  10.                 } 
  11.         } 
  12. n = mbfl_strlen(&string);//计算字符串的长度 

原来这个函数里只是验证编码
真正计算长度的函数是mbfl_strlen

 

 
    
  1. int  mbfl_strlen(mbfl_string *string) 
  2.         int len, n, m, k; 
  3.         unsigned char *p; 
  4.         const unsigned char *mbtab; 
  5.         const mbfl_encoding *encoding; 
  6.        //验证编码是否有效 
  7.         encoding = mbfl_no2encoding(string->no_encoding); 
  8.         if (encoding == NULL || string == NULL) { 
  9.                 return -1; 
  10.         }  
  11.   
  12.         len = 0; 
  13. //下面是几种不同的 
  14.         if (encoding->flag & MBFL_ENCTYPE_SBCS) { //是单字节字符集 直接返回 
  15.                 len = string->len; 
  16.         } else if (encoding->flag & (MBFL_ENCTYPE_WCS2BE | MBFL_ENCTYPE_WCS2LE)) { //宽字节每个字占2位 
  17.                 len = string->len/2; 
  18.         } else if (encoding->flag & (MBFL_ENCTYPE_WCS4BE | MBFL_ENCTYPE_WCS4LE)) { //宽字节每个字占4位 
  19.                 len = string->len/4; 
  20.         } else if (encoding->mblen_table != NULL) {   //mblen_table是做什么的没搞清楚,望赐教 
  21.                 mbtab = encoding->mblen_table; 
  22.                 n = 0; 
  23.                 p = string->val; 
  24.                 k = string->len; 
  25.                 /* count */ 
  26.                 if (p != NULL) { 
  27.                         while (n < k) { 
  28.                                 m = mbtab[*p]; 
  29.                                 n += m; 
  30.                                 p += m; 
  31.                                 len++; 
  32.                         }; 
  33.                 } 
  34.         } else { //其他编码 转换成mbfl_no_encoding_wchar再计算 
  35.                 /* wchar filter */ 
  36.                 mbfl_convert_filter *filter = mbfl_convert_filter_new( 
  37.                   string->no_encoding, 
  38.                   mbfl_no_encoding_wchar, 
  39.                   filter_count_output, 0, &len); 
  40.                 if (filter == NULL) { 
  41.                         return -1; 
  42.                 } 
  43.                 /* count */ 
  44.                 n = string->len; 
  45.                 p = string->val; 
  46.                 if (p != NULL) { 
  47.                         while (n > 0) { 
  48.                                 (*filter->filter_function)(*p++, filter); 
  49.                                 n--; 
  50.                         } 
  51.                 } 
  52.                 mbfl_convert_filter_delete(filter); 
  53.         } 
  54.   
  55.         return len; 


mb_strlen就是根据encoding不同的编码,来分别计算..好像可以计算世界大部分不同的编码格式
相比 mb_strlen ,strlen就简单的多多的多的多了
开始 没有找到 strlen定义的地方,
它在 Zend/zend_builtin_functions.c里定义的

 

 
    
  1. ZEND_FUNCTION(strlen) 
  2.         char *s1; 
  3.         int s1_len; 
  4.   
  5.         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s1, &s1_len) == FAILURE) { 
  6.                 return
  7.         }     
  8.   
  9.         RETVAL_LONG(s1_len); 

很简单 只是获取长度而已…

这下 他们俩的区别就很清楚了…

原文出处: http://www.imsiren.com/archives/196