2012-11-03 12 views
7

Pracuję nad projektem dotyczącym danych w językach obcych. Moje skrypty Perla działały dobrze.Dlaczego mój program Perla nie działa z kodowaniem Tie :: File i Unicode/UTF-8?

Potem chciałem użyć Tie :: File, ponieważ jest to zgrabna koncepcja (i oszczędza czas i kodowanie).

Wygląda na to, że Tie: Plik jest uszkodzony w Unicode/UTF-8 (chyba że czegoś brakuje).

Oto program, który przedstawia ten problem: (Dane są mieszanką angielskiego, greckim i hebrajskim):

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 

To daje ogromną kaskadę ostrzeżeń: tutaj jest kilka:

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 

Następnie drukuje to na 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 

Należy pamiętać, że pierwsze 10 linii są OK, ale linie 10 do 19 przybył znikąd !? Ponadto wyjście wiązanej pliku zawiera uszkodzone dane:

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



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

Coś jest bardzo źle tutaj. Albo coś mi brakuje, albo Tie: plik nie radzi sobie z Unicode/UTF-8? Używam Strawberry Perl 5.14 w systemie Windows 7.

Wiele TIA - Helen

Uwaga: zamieszczona na http://perlmonks.org/?node_id=1002104 też

+0

(Prawdopodobnie najprawdopodobniejszym) problemem może być to, że dane nie są prawidłowo zakodowane. Tak czy inaczej ostrzegają cię ostrzeżenia. – Mat

+0

@Mat: dane są poprawnie zakodowane. Tak jak powiedziałem powyżej, bez Tie :: File wszystko działa poprawnie. Zauważ też, że wydruk na STDOUT jest w porządku (dla pierwszych 9 linii). –

+0

Z jakiego edytora korzystasz i czy na pewno zapisujesz plik źródłowy jako UTF-8? (I nie musisz określać 'use feature qw ;', ponieważ to jest włączone przy użyciu 'use v5.14;'.) – titanofold

Odpowiedz

3

Sugestia chciałbym zrobić zależy w dużej mierze od rzeczywistego problemu, który próbujesz rozwiązać. Patrząc na to pytanie, w izolacji, nie miałbym zbyt wiele kodowania/dekodowania "magii" i po prostu używałbym surowych bajtów (ponieważ skrypt nie musi wiedzieć nic o samych znakach dla tego zadania). Poniższy wynik daje oczekiwany wynik, biorąc pod uwagę dane wejściowe i wyjściowe, które opisałeś.

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; 

Jednak, to prawdopodobnie chcą zrobić niektóre przetwarzania na ten tekst w środku. W takim przypadku potrzebujesz dekodowanych znaków. Jak widzę to są dwie opcje:

  1. Koduje ręcznie przed oddaniem do wiązanej tablicy
  2. dowiedzieć się, na czym polega problem z Tie :: File

Numer 2 jest prawdopodobnie nie- trywialne - szybkie skanowanie źródła Tie :: File i wygląda na to, że zawsze będzie podane bajty. Jedyną częścią, na którą możesz najwyraźniej wpłynąć, jest tryb bin pod numerem https://metacpan.org/source/TODDR/Tie-File-0.98/lib/Tie/File.pm#L111 - , który robisz pod numerem.

Tie :: File robi dużo seek połączeń perldoc ma do powiedzenia na poszukiwania (http://perldoc.perl.org/functions/seek.html):

Zanotuj w bajtach: nawet jeśli filehandle został ustawiony do pracy w postaci (na przykład przy użyciu otwartej warstwy: kodowanie (utf8)) funkcja tell() zwróci przesunięcia bajtów, a nie przesunięcia znaków (ponieważ implementacja spowoduje raczej spowolnienie funkcji seek() i tell()).

Wygląda na to, że Tie :: File używa długości znaków do określenia przesunięć bajtów dla rekordów. Dlatego może zakończyć się w środku sekwencji znaków UTF-8. To wydaje się być prawdopodobną przyczyną twoich błędów.

Ogólnie trzymam się z dala od binmode, gdy polegam na zewnętrznym module do odczytu/zapisu do uchwytu pliku - w tym przypadku miałbym proste pod wywoływanie Encode::encode('UTF-8', ...) na danych przed naciśnięciem na @ związany.

Wyjątkiem jest sytuacja, w której dokumentacja modułu wyraźnie określa zachowanie zdekodowanych danych lub jeśli źródło jest wystarczająco proste, aby zweryfikować zachowanie.

+1

dziękuję, to bardzo pouczające, I postanowił oznaczyć to przyjęcie. Mimo to, w końcu użyłem remisu z DB_file i DBM_filter, jak sugeruje remiah tutaj: http://perlmonks.org/?node_id=1002394. to działa. –

Powiązane problemy