假设我们有一个 UTF-8 字符串
$s
,我们需要缩短它,以便可以将其存储在 N 字节中。盲目地将其截断为 N 字节可能会弄乱它。但解码它以找到字符边界是一件很麻烦的事。有没有整齐的方法?
[编辑 20100414] 除了 S.Mark 的答案:
mb_strcut()
之外,我最近发现了另一个可以完成这项工作的函数:来自 intl扩展的
grapheme_extract($s, $n, GRAPHEME_EXTR_MAXBYTES);
。由于 intl 是 ICU 包装器,所以我对它很有信心。
我认为你不需要重新发明轮子,你可以只使用mb_strcut并确保首先将编码设置为UTF-8。
mb_internal_encoding('UTF-8');
echo mb_strcut("\xc2\x80\xc2\x80", 0, 3); //from index 0, cut to 3 bytes.
它的回归
\xc2\x80
因为\xc2\x80\xc2中,最后一个是无效的
编辑: S.Mark 的答案实际上比我的更好 - PHP 有一个(文档记录很差)内置函数可以准确解决这个问题。
原来的“回到位”答案如下:
这可以确保末尾不会出现“不完整的字符”,这是截断 UTF-8 时可能出错的主要问题。 不幸的是(正如 Andrew 在评论中提醒我的那样)也存在两个单独编码的 Unicode 代码点形成单个字符的情况(基本上,诸如重音符号之类的变音符号可以表示为修改前面字母的单独代码点)。
处理这种事情需要高级的 Unicode-Fu,这在 PHP 中不可用,甚至可能不适用于所有情况(那里有一些
weird脚本!),但幸运的是,它相对罕见,至少对于拉丁语来说-基于语言。
mb_strcut()
的测试。它并不能证明它正是我们正在寻找的东西,但我发现它非常有说服力。
<?php
ini_set('default_charset', 'UTF-8' );
$strs = array(
'Iñtërnâtiônàlizætiøn',
'החמאס: רוצים להשלים את עסקת שליט במהירות האפשרית',
'ايران لا ترى تغييرا في الموقف الأمريكي',
'独・米で死傷者を出した銃の乱射事件',
'國會預算處公布驚人的赤字數據後',
'이며 세계 경제 회복에 걸림돌이 되고 있다',
'В дагестанском лесном массиве южнее села Какашура',
'นายประสิทธิ์ รุ่งสะอาด ปลัดเทศบาล รักษาการแทนนายกเทศมนตรี ต.ท่าทองใหม่',
'ભારતીય ટીમનો સુવર્ણ યુગ : કિવીઝમાં પણ કમાલ',
'ཁམས་དཀར་མཛེས་ས་ཁུལ་དུ་རྒྱ་གཞུང་ལ་ཞི་བའི་ངོ་རྒོལ་',
'Χιόνια, βροχές και θυελλώδεις άνεμοι συνθέτουν το',
'Հայաստանում սկսվել է դատական համակարգի ձեւավորումը',
'რუსეთი ასევე გეგმავს სამხედრო');
for ( $i = 10; $i <= 30; $i += 5 ) {
foreach ($strs as $s) {
$t = mb_strcut($s, 0, $i, 'UTF-8');
print(
sprintf('%3s%3s ', mb_strlen($t, 'UTF-8'), mb_strlen($t, 'latin1'))
. ( mb_check_encoding($t, 'UTF-8') ? ' OK ' : ' Bad ' )
. $t . "\n");
}
}
?>
的答案是 mb_strcut()
之外,我最近发现了另一个函数可以完成类似的工作:来自
intl扩展的
grapheme_extract($s, $n, GRAPHEME_EXTR_MAXBYTES);
。
功能有点不同:mb_strcut()
文档声称它在最近的UTF-8字符边界处剪切,因此它不考虑多字符字素,而
grapheme_extract()
,otoh,则这样做。因此,根据您的需要,grapheme_extract()
可能更好(例如显示字符串)或mb_strcut()
可能更好(例如用于索引)。无论如何,尽管我会提到它。(由于 intl 是 ICU 包装器,所以我对它很有信心。)
然而,编码非常机械。 请参阅维基百科文章中的漂亮表格
编辑:Michael Borgwardt 向我们展示了如何在不解码整个字符串的情况下做到这一点。 聪明。