`
dengbaoleng
  • 浏览: 1131497 次
文章分类
社区版块
存档分类
最新评论

PHP内置函数分析之strlen 与 mb_strlen

 
阅读更多

声明:本文为斯人原创,全部为作者一一分析得之,有不对的地方望赐教。
博客地址:PHP技术博客在CSDN也会同步更新的哦.
欢迎转载,转载请注明出处

在PHP里 有两个计算 字符串个数的函数
一个是 strlen,一个是mb_strlen;
先来看看帮助手册的定义

  • strlen

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

  • mb_strlen

intmb_strlen( string$str[, string$encoding] )

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

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

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

写一个PHP程序来看看

[php]
$a="我是s斯t人";
echo strlen($a);
echo "<br>";
echo mb_strlen($a,'utf8');
[/php]


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

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

[c]
PHP_FUNCTION(mb_strlen)
{
int n;
mbfl_string string;
char *enc_name = NULL; int enc_name_len;

mbfl_string_init(&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.no_language = MBSTRG(language);
if (enc_name == NULL) { string.no_encoding = MBSTRG(current_internal_encoding);
} else {
string.no_encoding = mbfl_name2no_encoding(enc_name);
if (string.no_encoding == mbfl_no_encoding_invalid) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown encoding \"%s\"", enc_name); RETURN_FALSE;
}
}

n = mbfl_strlen(&string);
if (n >= 0) {
RETVAL_LONG(n);
} else {
RETVAL_FALSE;
}
}
[/c]

mbfl_string 是一个结构体

[c]
typedef struct _mbfl_string {
enum mbfl_no_language no_language;
enum mbfl_no_encoding no_encoding;
unsigned char *val; unsigned int len;
} mbfl_string;

[/c]


看定义就知道,这两个保存了当前字符串的语言,编码,保存的值和长度
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;

[c]

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

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

[c]
int mbfl_strlen(mbfl_string *string)
{
int len, n, m, k;
unsigned char *p;
const unsigned char *mbtab;
const mbfl_encoding *encoding;
//验证编码是否有效
encoding = mbfl_no2encoding(string->no_encoding);
if (encoding == NULL || string == NULL) {
return -1;
}

len = 0;
//下面是几种不同的
if (encoding->flag & MBFL_ENCTYPE_SBCS) {//是单字节字符集 直接返回
len = string->len;
} else if (encoding->flag & (MBFL_ENCTYPE_WCS2BE | MBFL_ENCTYPE_WCS2LE)) { //宽字节每个字占2位
len = string->len/2;
} else if (encoding->flag & (MBFL_ENCTYPE_WCS4BE | MBFL_ENCTYPE_WCS4LE)) {//宽字节每个字占4位
len = string->len/4;
} else if (encoding->mblen_table != NULL) { //mblen_table是做什么的没搞清楚,望赐教
mbtab = encoding->mblen_table;
n = 0;
p = string->val;
k = string->len;
/* count */
if (p != NULL) {
while (n < k) {
m = mbtab[*p];
n += m;
p += m;
len++;
};
}
} else { //其他编码 转换成mbfl_no_encoding_wchar再计算
/* wchar filter */
mbfl_convert_filter *filter = mbfl_convert_filter_new(
string->no_encoding,
mbfl_no_encoding_wchar,
filter_count_output, 0, &len);
if (filter == NULL) {
return -1;
}
/* count */
n = string->len;
p = string->val;
if (p != NULL) {
while (n > 0) {
(*filter->filter_function)(*p++, filter);
n--;
}
}
mbfl_convert_filter_delete(filter);
}

return len;
}
[/c]

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

[c]
ZEND_FUNCTION(strlen)
{
char *s1;
int s1_len;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s1, &s1_len) == FAILURE) {
return;
}

RETVAL_LONG(s1_len);
}
[/c]

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

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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics