我有utf8字节序列,需要修剪它说30bytes.这可能导致最后的序列不完整.我需要弄清楚如何删除不完整的序列.
例如
$b="\x{263a}\x{263b}\x{263c}";
my $sstr;
print STDERR "length in utf8 bytes =" . length(Encode::encode_utf8($b)) . "\n";
{
use bytes;
$sstr= substr($b,0,29);
}
#After this $sstr contains "\342\230\272\342"\0
# How to remove \342 from the end
Run Code Online (Sandbox Code Playgroud)
UTF-8有一些简洁的属性,允许我们在处理UTF-8而不是字符时做你想做的事.首先,你需要UTF-8.
use Encode qw( encode_utf8 );
my $bytes = encode_utf8($str);
Run Code Online (Sandbox Code Playgroud)
现在,在代码点之间进行拆分.每个代码点的UTF-8编码将一个字节匹配启动0b0xxxxxxx或者0b11xxxxxx,你永远不会找到一个码点的中间那些字节.这意味着你想要之前截断
[\x00-\x7F\xC0-\xFF]
Run Code Online (Sandbox Code Playgroud)
我们一起得到:
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"
Run Code Online (Sandbox Code Playgroud)
太棒了,对吗?不.以上可以在字形中间截断.字形(特别是"扩展的字形簇")是人们将其视为单个视觉单元的东西.例如,"é"是字形,但可以使用两个代码点("\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"
Run Code Online (Sandbox Code Playgroud)
首先,请不要使用bytes(并且永远不要假设 Perl 中的任何内部编码)。正如文档所述: 这个编译指示反映了将 Unicode 合并到 perl 中的早期尝试,并且已被取代 <...> 强烈建议不要将此模块用于除调试目的以外的任何用途。
要在行尾去除不完整的序列,假设它包含八位字节,请使用 的Encode::decode处理Encode::FB_QUIET模式在遇到无效序列时停止处理,然后将结果编码回来:
my $valid = Encode::decode('utf8', $sstr, Encode::FB_QUIET);
$sstr = Encode::encode('utf8', $valid);
Run Code Online (Sandbox Code Playgroud)
请注意,如果您计划将来将其与其他编码一起使用,则并非所有编码都支持此处理方法。