2012-06-08 35 views
3

我有utf8字節序列,需要修剪它說30bytes。這可能導致最後的序列不完整。我需要弄清楚如何刪除不完整的序列。perl-修剪utf8字節到'長度'和消毒數據

e.g

$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 

回答

3

首先,請不要使用bytes(從不認爲在Perl任何內部編碼)。正如文檔所述:此編譯指示反映了早期將Unicode併入Perl的嘗試,並已被取代< ...>強烈建議不要使用此模塊進行除調試以外的任何其他操作。

要在行尾剝不完整序列,假設它包含個字節,使用Encode::decodeEncode::FB_QUIET處理模式,一旦你打無效的序列,然後只編碼結果返回到停止處理:

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

注意,如果你打算將來使用它與另一種編碼,並不是所有的編碼都可以支持這種處理方法。

+0

太棒了!謝謝 。當確定$ b使用utf8編碼時,「使用字節」是不安全的。 – user1444975

+0

不可以。沒有人保證內部Perl總是使用UTF-8。使用'Encode :: encode('utf8',...)'(或'Encode :: encode_utf8')。 –

+0

不可以。使用'encode'總是安全的,所以爲什麼你要使用一半時間錯誤的東西,即使你現在知道它是正確的。 – ikegami

6

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」!如果這是不可接受的,上述解決方案也不是。 (奧列格的解決方案也遭受同樣的問題。)

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