1. 程式人生 > >PHP內建函式分析之strlen 與 mb_strlen

PHP內建函式分析之strlen 與 mb_strlen

宣告:本文為斯人原創,全部為作者一一分析得之,有不對的地方望賜教。
部落格地址:PHP技術部落格 在CSDN也會同步更新的哦. 歡迎轉載,轉載請註明出處 

在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程式來看看

[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]

很簡單 只是獲取長度而已...

這下 他們倆的區別就很清楚了...