2012-11-03 4 views
7

Ich arbeite an einem Projekt, das sich mit Daten in Fremdsprachen beschäftigt. Meine Perl-Skripte liefen gut.Warum scheitert mein Perl-Programm mit der Encodierung von Tie :: File und Unicode/UTF-8?

Ich wollte dann Tie :: File verwenden, da dies ein nettes Konzept ist (und spart Zeit und Codierung).

Es scheint, dass Tie: Datei unter Unicode/UTF-8 fehlschlägt (es sei denn, ich vermisse etwas). Hier

ist ein Programm, das das Problem zeigt: (Die Daten ist eine Mischung aus Englisch, Griechisch und Hebräisch):

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 

dies eine große Kaskade von Warnungen erzeugt: hier einige sind:

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 

Dann druckt er dies auf 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 

Beachten sie, dass die ersten 10 Zeilen in Ordnung sind, aber Linien 10 bis 19 kamen aus dem Nichts !? Darüber hinaus ist die Ausgabe der gebundenen Datei enthält beschädigte Daten:

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



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

Etwas ist sehr falsch hier. Entweder fehlt mir etwas, oder Krawatte: Datei kann nicht mit Unicode/UTF-8 umgehen? Ich verwende Strawberry Perl 5.14 auf einem Windows 7-System.

Viele TIA - Helen

Hinweis: posted on http://perlmonks.org/?node_id=1002104 auch

+0

Das (wahrscheinlich wahrscheinlichste) Problem könnte sein, dass Ihre Daten nicht richtig codiert sind. Das sagen dir die Warnungen trotzdem. – Mat

+0

@Mat: Die Daten sind richtig codiert. Wie ich oben sagte, ohne Tie :: File funktioniert alles gut. Beachten Sie auch, dass der Ausdruck auf STDOUT in Ordnung ist (für die ersten 9 Zeilen) –

+0

Welchen Editor benutzen Sie und sind Sie sicher, dass er die Quelldatei als UTF-8 speichert? (Und Sie müssen nicht angeben, verwenden Sie Feature qw ; ', da dies mit' v5.14; 'aktiviert wird.) – titanofold

Antwort

3

Der Vorschlag, den ich hängt sehr stark von dem tatsächlichen Problem machen würde Sie zu lösen versuchen. Betrachtet man diese Frage isoliert, würde ich nicht so viel Kodierung/Dekodierung "Magie" haben und würde einfach die rohen Bytes verwenden (da das Skript nichts über die Zeichen selbst für diese Aufgabe wissen muss). Das Folgende erzeugt das erwartete Ergebnis bei der Eingabe und Ausgabe, die Sie beschrieben haben.

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; 

jedoch, werden Sie wahrscheinlich wollen in der Mitte einige Verarbeitung auf diesem Text zu tun. In diesem Fall möchten Sie dekodierte Zeichen. Wie ich es sehe gibt es zwei Möglichkeiten:

  1. Encode manuell, bevor zum gebundenen Array
  2. herauszufinden, was das Problem mit Krawatte Handoffs ist :: File

Nummer 2 ist wahrscheinlich nicht trivial - ein schneller Scan der Tie :: File-Quelle und es sieht so aus, als würde davon ausgegangen, dass es immer Bytes bekommen wird. Der einzige Teil, den Sie scheinbar beeinflussen können, ist der Bin-Modus bei https://metacpan.org/source/TODDR/Tie-File-0.98/lib/Tie/File.pm#L111 - , den Sie tun.

Tie :: File tut viel seek Anrufe, Perldoc dies auf suchen zu sagen hat (http://perldoc.perl.org/functions/seek.html):

Notiere die in Bytes: auch wenn die Dateikennung auf Zeichen für den Betrieb gesetzt wurde (zum Beispiel mit dem: encoding (utf8) open layer), liefert tell() Byte-Offsets, nicht Zeichen-Offsets (weil die Implementierung das seek() und tell() eher langsam macht).

So scheint es, dass Tie :: File Zeichenlängen verwendet, um seine Byte-Offsets für Datensätze zu bestimmen. Daher kann es in der Mitte einer UTF-8-Zeichenfolge enden. Dies scheint eine wahrscheinliche Ursache für Ihre Fehler zu sein.

Im Allgemeinen, ich bleibe weg von binmode wenn Sie auf einem externen Modul zu lesen/zu einem Datei-Handle lesen - in diesem Fall hätte ich eine einfache Unter Berufung Encode::encode('UTF-8', ...) auf den Daten vor dem Drücken auf @tied.

Ausnahme ist, wo die Dokumentation des Moduls deutlich das Verhalten für decodierte Daten angibt oder wenn die Quelle einfach genug ist, um das Verhalten zu überprüfen.

+1

danke, das ist sehr lehrreich, ich beschlossen, es akzeptiert zu markieren. Obwohl, endete ich mit tie mit DB_file und DBM_filter, wie von remiah hier vorgeschlagen: http://perlmonks.org/?node_id=1002394. das macht den Job. –

Verwandte Themen