7

私は外国語のデータを扱うプロジェクトに取り組んでいます。Perl スクリプトは問題なく動作していました。

次に、Tie::File を使用したいと思いました。これは、これがきちんとした概念である (そして、時間とコーディングを節約できる) からです。

Tie:File は Unicode/UTF-8 で失敗しているようです (何か不足していない限り)。

問題を示すプログラムは次のとおりです: (データは英語、ギリシャ語、ヘブライ語が混在しています):

use strict;
 use warnings;
 use 5.014; 
 use Win32::Console;
 use autodie; 
 use warnings qw< FATAL utf8 >;
 use Carp;
 use Carp::Always;
 use utf8;
 use feature        qw< unicode_strings>;
 use charnames      qw< :full>;
use Tie::File;

my ($i);
my ( $FileName);
my (@Tied);
binmode STDOUT, ':unix:utf8';
binmode STDERR, ':unix:utf8';
binmode $DB::OUT, ':unix:utf8' if $DB::OUT; # for the debugger
Win32::Console::OutputCP(65001);         # Set the console code page to UTF8

$FileName = 'E:\\My Documents\\Technical\\Perl\\Eclipse workspace\\Work\\'.
        'Tie File test res.txt';
tie @Tied, 'Tie::File', $FileName, recsep => "\x0D\x0A", discipline => ':encoding(utf8)'
            or confess 'tie @Tied failed';
$i =0;
while (<DATA>) {
    chomp;
    $Tied[$i] = $_;
    ++$i;
} # end while (<DATA>) 
$i =0;
foreach (@Tied) {
    say "$i $Tied[$i]";
    ++$i;
} # end foreach (@Tied)
untie $FileName;
__DATA__
τι κάνετε;
πάρτε το ή αφήστε το
שלום חברים
abc לא כןכן efg
מתי ולאן This is it
מעכשיו לעכשיו 
Σήμερα είναι Τρίτη
Θέλω να φάω
τι κάνετε;
שורה מס' 5

これにより、大量の警告がカスケードされます。以下にその一部を示します。

utf8 "\xCE" does not map to Unicode at F:/Win7programs/Dwimperl/perl/lib/Tie/File.pm line 917
        Tie::File::_read_record('Tie::File=HASH(0x24cb72c)') called at F:/Win7programs/Dwimper
l/perl/lib/Tie/File.pm line 175
        Tie::File::_fetch('Tie::File=HASH(0x24cb72c)', 0) called at F:/Win7programs/Dwimperl/p
erl/lib/Tie/File.pm line 210
        Tie::File::STORE('Tie::File=HASH(0x24cb72c)', 0, 'τι κάνετε;') called at tie file test
.pl line 31
utf8 "\xCF" does not map to Unicode at F:/Win7programs/Dwimperl/perl/lib/Tie/File.pm line 917
        Tie::File::_read_record('Tie::File=HASH(0x24cb72c)') called at F:/Win7programs/Dwimper
l/perl/lib/Tie/File.pm line 175
        Tie::File::_fetch('Tie::File=HASH(0x24cb72c)', 0) called at F:/Win7programs/Dwimperl/p
erl/lib/Tie/File.pm line 210
        Tie::File::STORE('Tie::File=HASH(0x24cb72c)', 0, 'τι κάνετε;') called at tie file test
.pl line 31
utf8 "\xD7" does not map to Unicode at F:/Win7programs/Dwimperl/perl/lib/Tie/File.pm line 917
        Tie::File::_read_record('Tie::File=HASH(0x24cb72c)') called at F:/Win7programs/Dwimper
l/perl/lib/Tie/File.pm line 175
        Tie::File::_fetch('Tie::File=HASH(0x24cb72c)', 0) called at F:/Win7programs/Dwimperl/p
erl/lib/Tie/File.pm line 210
        Tie::File::STORE('Tie::File=HASH(0x24cb72c)', 0, 'τι κάνετε;') called at tie file test
.pl line 31
utf8 "\xD7" does not map to Unicode at F:/Win7programs/Dwimperl/perl/lib/Tie/File.pm line 917
        Tie::File::_read_record('Tie::File=HASH(0x24cb72c)') called at F:/Win7programs/Dwimper
l/perl/lib/Tie/File.pm line 175
        Tie::File::_fetch('Tie::File=HASH(0x24cb72c)', 0) called at F:/Win7programs/Dwimperl/p
erl/lib/Tie/File.pm line 210
        Tie::File::STORE('Tie::File=HASH(0x24cb72c)', 0, 'τι κάνετε;') called at tie file test
.pl line 31

次に、これを STDOUT に出力します。

0 τι κάνετε;
1 πάρτε το ή αφήστε το
2 שלום חברים
3 abc לא כןכן efg
4 מתי ולאן This is it
5 מעכשיו לעכשיו
6 Σήμερα είναι Τρίτη
7 Θέλω να φάω
8 τι κάνετε;
9 שורה מס' 5
10
11
12
13
14 \xA4\xΘέλω\xA8\x

15
16
17
18

19

最初の 10 行は問題ありませんが、10 行目から 19 行目まではどこからともなく来ていることに注意してください!? さらに、関連付けられたファイルの出力には、破損したデータが含まれています。

 τι κάνϏN͏Ŏՠτήστε של חברءbc לؗܗࠗܗߠeמתולאן This is מעיו לעכ؎Ďώݎ֏ναι ΤρΘέώގѠφϏŎ٠κτε;שרה מס'



\xA4\xΘέλω\xA8\x

ここで何かが非常に間違っています。何か足りないのですか、それとも Tie:File が Unicode/UTF-8 に対応していないのですか? Windows 7 システムで Strawberry Perl 5.14 を実行しています。

多くのTIA - ヘレン

注: http://perlmonks.org/?node_id=1002104にも投稿されています。

4

1 に答える 1

3

私が行う提案は、解決しようとしている実際の問題に大きく依存します。この質問を単独で見ると、エンコード/デコードの「魔法」はそれほど多くなく、単純に生のバイトを使用します(スクリプトはこのタスクの文字自体について何も知る必要がないため)。以下は、説明した入力と出力が与えられた場合に期待される結果を生成します。

use v5.014;
use warnings;
use autodie;

use Carp::Always;
use Tie::File;

my $file_in = 'test_in.txt';
my $file_out = 'test_tie.txt';

unlink $file_out;

tie my @tied, 'Tie::File', $file_out, recsep => "\x0D\x0A" or die 'tie failed';

open my $fh, '<', $file_in;
while (my $line = <$fh>) {
    chomp $line;
    push @tied, $line;
}
close $fh;

my $i = 0;
say $i++ . ' ' . $_ foreach @tied;

untie @tied;

ただし、おそらく途中でそのテキストに対して何らかの処理を行いたいと思うでしょう。その場合、デコードされた文字が必要です。私が見たように、2つのオプションがあります:

  1. 結合された配列に渡す前に手動でエンコードする
  2. Tie::File で何が問題なのかを把握する

番号 2 はおそらく自明ではありません - Tie::File ソースを簡単にスキャンすると、常にバイトが与えられると仮定しているように見えます。一見影響を与えることができる唯一の部分は、https://metacpan.org/source/TODDR/Tie-File-0.98/lib/Tie/File.pm#L111の binmode です-あなたがやっている.

Tie::File は多くのseek呼び出しを行います。perldoc はシークについて次のように述べています ( http://perldoc.perl.org/functions/seek.html ):

バイト単位に注意してください: ファイルハンドルが文字で動作するように設定されていても (たとえば、:encoding(utf8) オープンレイヤーを使用して)、tell() は文字オフセットではなくバイトオフセットを返します (これを実装すると seek( がレンダリングされるため)。 ) と tell() はかなり遅い)。

そのため、Tie::File は文字長を使用してレコードのバイト オフセットを決定しているようです。したがって、UTF-8 文字シーケンスの途中で終了する可能性があります。これは、エラーの原因と思われます。

一般にbinmode、ファイル ハンドルの読み取り/書き込みを外部モジュールに依存する場合は避けます。この場合、Encode::encode('UTF-8', ...)@tied にプッシュする前に、データに対して単純なサブ呼び出しを行います。

例外は、モジュールのドキュメントにデコードされたデータの動作が明確に記載されている場合、またはソースが動作を確認できるほど単純である場合です。

于 2012-11-04T22:45:04.070 に答える