在處理多字節字符串時, mb_strcut是PHP 中一個非常實用的函數。它的主要作用是從一個多字節字符串中按字節數截取子字符串。然而,當字符串中包含如emoji 這類由多個字節組成的特殊字符時,使用mb_strcut就需要格外小心。
mb_strcut(string $string, int $start, ?int $length = null, ?string $encoding = null): string
此函數基於字節偏移(而非字符偏移)來截取字符串。
與mb_substr不同的是, mb_strcut實際上是一個「字節安全」的版本,但它在遇到部分多字節字符(如emoji)被切斷時,會直接截斷而不是補全字符。
來看一個例子:
<code> $str = "Hello ?? World!"; $cut = mb_strcut($str, 0, 9, 'UTF-8'); echo $cut; </code>你可能期望輸出是Hello ?? ,但實際上可能看到的是一個破損的字符串,甚至出現亂碼。這是因為??在UTF-8 編碼下是一個4 字節字符,而mb_strcut可能會在其中間字節截斷。
emoji 通常是4 字節甚至更長(例如復合emoji,如???????????)。如果你只按字節切割,而不考慮字符邊界,就可能出現:
輸出中含有非法字符;
瀏覽器顯示為亂碼或問號;
數據庫可能報錯(尤其是嚴格模式下);
JSON 編碼可能失敗。
如果你的目標是顯示一段含emoji 的文本預覽(比如微博、評論等內容摘要),可以考慮以下幾種方式:
如果你不介意以「字符」為單位截取,可以使用mb_substr ,它能確保不會破壞字符邊界:
<code> $str = "Hello ?? World!"; $preview = mb_substr($str, 0, 7, 'UTF-8'); echo $preview; </code>這會輸出完整的字符,而不是殘缺的字節。
若你堅持使用mb_strcut (比如為了控製字節數),可以在截斷後用正則移除不完整字符:
<code> $str = "Hello ?? World!"; $cut = mb_strcut($str, 0, 9, 'UTF-8'); // 用正則清理非法字符
$clean = preg_replace('/[\xC0-\xFF][\x80-\xBF]*$/', '', $cut);
echo $clean;
</code>
這段代碼會嘗試移除結尾處可能被截斷的不完整多字節字符。
PHP 的intl擴展提供了字符邊界檢測,適用於處理複雜多字節字符:
<code> $str = "Hello ?? World!"; $breakIterator = IntlBreakIterator::createCharacterInstance('en'); $breakIterator->setText($str); $bytes = 0;
$limit = 9;
$pos = 0;
foreach ($breakIterator as $boundary) {
$chunk = mb_substr($str, $pos, $boundary - $pos, 'UTF-8');
$chunkBytes = strlen($chunk);
if ($bytes + $chunkBytes > $limit) {
break;
}
$bytes += $chunkBytes;
$pos = $boundary;
}
$preview = mb_substr($str, 0, $pos, 'UTF-8');
echo $preview;
</code>
這樣可以保證你截取的字符串在字節限制下仍是字符完整的,適合用於國際化項目或複雜文本處理。
當字符串中包含emoji 或其他多字節字符時,使用mb_strcut截取字符串需要特別注意:
它按字節截取,可能破壞emoji;
截斷後需清理非法字符或結合正則修復;
使用mb_substr更安全,但不能精確控製字節;
推薦用IntlBreakIterator來確保截斷位置合法。
務必在用戶界面、數據庫入庫、接口輸出等環節測試emoji 處理的完整性和兼容性,避免出現亂碼或數據異常的問題。
更多關於字符處理的最佳實踐可參考文檔或訪問https://gitbox.net/dev/mbstring 。