在处理多字节字符串时,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。