4

バイトの 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
4

2 に答える 2

6

UTF-8 には、文字ではなく UTF-8 を扱いながら、ユーザーが望むことを実行できる優れたプロパティがいくつかあります。まず、UTF-8 が必要です。

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

次に、コードポイント間で分割します。0b0xxxxxxxすべてのコード ポイントの UTF-8 エンコーディングは、またはに一致するバイトで始まり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"

いいですよね?いいえ。上記は、書記素の途中で切り捨てられる可能性があります。書記素 (具体的には、「拡張書記素クラスター」) は、誰かが単一の視覚的単位として認識するものです。たとえば、「é」は書記素ですが、2 つのコードポイントを使用してエンコードできます ( "\x{0065}\x{0301}")。2 つのコード ポイントの間を切り取ると、有効な UTF-8 になりますが、「é」は「e」になります。それが受け入れられない場合は、上記の解決策もありません。(オレグのソリューションも同じ問題に悩まされています。)

残念ながら、UTF-8 のプロパティは、ここで私たちを助けるにはもはや十分ではありません。一度に 1 つの書記素を取得し、収まらなくなるまで出力に追加する必要があります。

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"
于 2012-06-08T19:18:29.563 に答える
4

まず、使用しないでくださいbytes(Perlの内部エンコーディングを想定しないでください)。ドキュメントにあるように:このプラグマは、Unicodeをperlに組み込む初期の試みを反映しており、その後、<...>デバッグ目的以外でこのモジュールを使用することは強くお勧めしません。

オクテットが含まれていると仮定して、行末の不完全なシーケンスを削除するには、Encode::decodeEncode::FB_QUIET処理モードを使用して、無効なシーケンスに到達したら処理を停止し、結果をエンコードして戻します。

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

将来、別のエンコーディングで使用する予定がある場合、すべてのエンコーディングがこの処理方法をサポートしているとは限らないことに注意してください。

于 2012-06-08T17:52:58.330 に答える