【问题标题】:perl- trim utf8 bytes to 'length' and sanitize the dataperl- 将 utf8 字节修剪为“长度”并清理数据
【发布时间】:2012-06-08 17:01:55
【问题描述】:

我有 utf8 字节序列,需要将其修剪为 30 字节。这可能导致最后的序列不完整。我需要弄清楚如何删除不完整的序列。

例如

$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

【问题讨论】:

    标签: perl utf-8


    【解决方案1】:

    UTF-8 有一些简洁的属性,允许我们在处理 UTF-8 而不是字符时做你想做的事。所以首先,你需要 UTF-8。

    use Encode qw( encode_utf8 );
    my $bytes = encode_utf8($str);
    

    现在,在代码点之间进行拆分。每个代码点的 UTF-8 编码都会以匹配 0b0xxxxxxx0b11xxxxxx 的字节开始,您永远不会在代码点的中间找到这些字节。这意味着你想在之前截断

    [\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”!如果这不可接受,则上述解决方案也不是。 (Oleg 的解决方案也存在同样的问题。)

    不幸的是,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"
    

    【讨论】:

      【解决方案2】:

      首先,请不要使用bytes(并且永远不要假设在 Perl 中有任何内部编码)。如文档所述:此编译指示反映了将 Unicode 合并到 perl 中的早期尝试,并且已被取代<...>强烈建议不要将此模块用于除调试目的之外的任何其他用途。

      要在行尾去除不完整的序列,假设它包含八位字节,使用Encode::decodeEncode::FB_QUIET 处理模式在遇到无效序列时停止处理,然后将结果编码回来:

      my $valid = Encode::decode('utf8', $sstr, Encode::FB_QUIET);
      $sstr = Encode::encode('utf8', $valid);
      

      请注意,如果您计划将来将其与另一种编码一起使用,则并非所有编码都支持这种处理方法。

      【讨论】:

      • 太棒了!!谢谢 。当确定 $b 是 utf8 编码时,“使用字节”是否安全。
      • 没有。没有人保证 Perl 内部总是使用 UTF-8。使用Encode::encode('utf8', ...)(或Encode::encode_utf8)。
      • 没有。使用encode 总是安全的,那么为什么你要在一半的时候使用其他错误的东西,即使你现在知道它是正确的。
      猜你喜欢
      • 2021-04-06
      • 1970-01-01
      • 2018-05-01
      • 2012-01-19
      • 1970-01-01
      • 1970-01-01
      • 2015-04-11
      • 1970-01-01
      • 2017-03-17
      相关资源
      最近更新 更多