UTF-8有一些整潔的屬性,可以讓我們在處理UTF-8而不是字符時做你想做的事情。所以首先,你需要UTF-8。
use Encode qw(encode_utf8);
my $bytes = encode_utf8($str);
現在,分割代碼點。每個編碼點的UTF-8編碼將以匹配0b0xxxxxxx
或0b11xxxxxx
的字節開頭,並且您將永遠不會在代碼點中間找到這些字節。這意味着你要前
[\x00-\x7F\xC0-\xFF]
截斷總之,我們得到:
use Encode qw(encode_utf8);
my $max_bytes = 8;
my $str = "\x{263a}\x{263b}\x{263c}"; # ☺☻☼
my $bytes = encode_utf8($str);
$bytes =~ s/^.{0,$max_bytes}(?![^\x00-\x7F\xC0-\xFF])\K.*//s;
# $bytes contains encode_utf8("\x{263a}\x{263b}")
# instead of encode_utf8("\x{263a}\x{263b}") . "\xE2\x98"
大,是嗎?不。以上可以在字形中間截斷。一個字形(特別是一個「擴展的字形集羣」)是某人將其視爲單個視覺單位的東西。例如,「é」是一個字形,但可以使用兩個代碼點編碼("\x{0065}\x{0301}"
)。如果你在兩個代碼點之間切換,它將是有效的UTF-8,但是「é」會變成「e」!如果這是不可接受的,上述解決方案也不是。 (奧列格的解決方案也遭受同樣的問題。)
不幸的是,UTF-8的屬性不足以幫助我們在這裏。我們需要一次抓取一個字形,然後將其添加到輸出,直到我們無法放入一個。
my $max_bytes = 6;
my $str = "abcd\x{0065}\x{0301}fg"; # abcdéfg
my $bytes = '';
my $bytes_left = $max_bytes;
while ($str =~ /(\X)/g) {
my $grapheme = $1;
my $grapheme_bytes = encode_utf8($grapheme);
$bytes_left -= length($grapheme_bytes);
last if $bytes_left < 0;
$bytes .= $grapheme_bytes;
}
# $bytes contains encode_utf8("abcd")
# instead of encode_utf8("abcde")
# or encode_utf8("abcde") . "\xCC"
太棒了!謝謝 。當確定$ b使用utf8編碼時,「使用字節」是不安全的。 – user1444975
不可以。沒有人保證內部Perl總是使用UTF-8。使用'Encode :: encode('utf8',...)'(或'Encode :: encode_utf8')。 –
不可以。使用'encode'總是安全的,所以爲什麼你要使用一半時間錯誤的東西,即使你現在知道它是正確的。 – ikegami