in 程式小心得

[php] grapheme_strlen vs mb_strlen vs strlen

前言

小時候在寫 PHP 時要看字串長度,常常不懂事的用 strlen 來作判斷,但是在遇到中日韓這種會把 2 bytes 當一個字的長度時,這時候字串的長度又不準了。這時學會了使用 mb_strlen 這個 function,感覺長度這樣用,就準了。但有趣的事是,竟然近期又出現了 grapheme_strlen 。崩潰,不過就是算個長度而已,為什麼有這麼多的方法。

讓我們來寫個小程式來實驗一下

<?php
$str = "這是中文 english 混合測試";
var_dump($str);
var_dump(
    mb_strlen($str, 'UTF-8'),
    grapheme_strlen($str),
    strlen($str)
);
?>

結果如下:

$ php /tmp/test.php
string(33) "這是中文 english 混合測試"
int(17)
int(17)
int(33)

看起來很合理啊,用 strlen 結果是 33 個字元,因為他是算 bytes。mb_strlen 17 個字,即使是中英文混合,看起來算字數也是沒問題的!mb_strlen 和 grapheme_strlen 目前看起來結果是一樣的。

接下來再一個小實驗

<?php
$str = json_decode('"e\u0301 = \u00E9"');
var_dump($str);
var_dump(
    mb_strlen($str, 'UTF-8'),
    grapheme_strlen($str),
    strlen($str)
);
?>

結果如下:

$ php /tmp/test.php
string(8) "é = é"
int(6)
int(5)
int(8)

WTF,竟然有三個不同的數字,畫面上來看,明明就只有五個字,為什麼 mb_strlen 會算出來六個字呢!

原來是 Grapheme Cluster 在作怪

Grapheme Cluster 就類似中文一樣,會把二個字當作一個字。所以畫面上看起來是一個字,但實際上在 unicode 是可以用二個字來表示,所以可以看見範例二中顯示的字是一樣,但 unicode 是不同的。而這樣就會造成 mutlibyte 的 function 計算長度有問題。

結論就是....

如果有考慮中日韓以外的語系的話,可以使用 grapheme_strlen 。另外必須要注意,如果傳進來的參數不是 utf8 的字的話,他的回傳值是 null ,而這部份與 mb_strlen 的結果不一致。

參考文章

Write a Comment

Comment

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料