[php] grapheme_strlen vs mb_strlen vs strlen

前言

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

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

[code lang=”php”] <?php $str = “這是中文 english 混合測試”; var_dump($str); var_dump( mb_strlen($str, ‘UTF-8’), grapheme_strlen($str), strlen($str) ); ?> [/code] 結果如下: [code lang=”bash”] $ php /tmp/test.php string(33) “這是中文 english 混合測試” int(17) int(17) int(33) [/code] 看起來很合理啊,用 strlen 結果是 33 個字元,因為他是算 bytes。mb_strlen 17 個字,即使是中英文混合,看起來算字數也是沒問題的!mb_strlen 和 grapheme_strlen 目前看起來結果是一樣的。

接下來再一個小實驗

[code lang=”php”] <?php $str = json_decode(‘“e\u0301 = \u00E9”‘); var_dump($str); var_dump( mb_strlen($str, ‘UTF-8’), grapheme_strlen($str), strlen($str) ); ?> [/code] 結果如下: [code lang=”bash”] $ php /tmp/test.php string(8) “é = é” int(6) int(5) int(8) [/code] WTF,竟然有三個不同的數字,畫面上來看,明明就只有五個字,為什麼 mb_strlen 會算出來六個字呢!

原來是 Grapheme Cluster 在作怪

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

結論就是….

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

參考文章