2012-11-03 7 views
7

Sto lavorando a un progetto che si occupa di dati in lingue straniere. I miei script Perl funzionavano bene.Perché il mio programma Perl non funziona con la codifica Tie :: File e Unicode/UTF-8?

Ho quindi voluto utilizzare Tie :: File, poiché questo è un concetto accurato (e consente di risparmiare tempo e codice).

Sembra che Cravatta: il file non funziona in Unicode/UTF-8 (a meno che manchi qualcosa).

Ecco un programma che raffigura il problema: (I dati sono un misto di inglese, greco ed ebraico):

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 

Questo produce un enorme cascata di avvertimenti: ecco alcune:

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 

Poi esso stampa questo su 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 

nota che le prime 10 linee sono OK, ma le linee Dal 10 al 19 venuto dal nulla !? Inoltre, l'output del file legato contiene dati danneggiati:

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



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

Qualcosa di molto sbagliato qui. O mi manca qualcosa o Tie: File non può far fronte con Unicode/UTF-8? Sto eseguendo Strawberry Perl 5.14 su un sistema Windows 7.

Molti TIA - Helen

Nota: Pubblicato il http://perlmonks.org/?node_id=1002104, anche

+0

Il problema (probabilmente più probabile) potrebbe essere che i dati non siano codificati correttamente per iniziare. Questo è quello che gli avvertimenti ti dicono comunque. – Mat

+0

@Mat: i dati sono correttamente codificati. Come ho detto sopra, senza Tie :: File tutto funziona perfettamente. Si noti inoltre che la stampa su STDOUT va bene (per le prime 9 righe) –

+0

Quale editor stai usando e sei certo che sta salvando il file sorgente come UTF-8? (E non è necessario specificare 'use function qw ;' dato che viene abilitato con 'use v5.14;'.) – titanofold

risposta

3

Il suggerimento che potrei fare dipende molto il problema reale si sta cercando di risolvere. Guardando questa domanda in isolamento, non avrei così tanto codifica/decodifica 'magica' e semplicemente userei i byte grezzi (dato che lo script non ha bisogno di sapere nulla sui personaggi stessi per questa attività). Il seguente produce il risultato atteso dato l'input e l'output che hai descritto.

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; 

Tuttavia, probabilmente si vuole fare un po 'di elaborazione su quel testo al centro. Nel qual caso vuoi caratteri decodificati. Come la vedo io ci sono due opzioni:

  1. Encode manualmente prima di consegnare a mano alla matrice legata
  2. capire quale sia il problema è con Tie :: File

Numero 2 è probabilmente non banale - una scansione veloce della sorgente di Tie :: File e sembra che presupponga che saranno sempre dati byte. L'unica parte che puoi apparentemente influenzare è la binmode su https://metacpan.org/source/TODDR/Tie-File-0.98/lib/Tie/File.pm#L111 - che stai facendo.

Tie :: File fa un sacco di chiamate seek, perldoc ha questo da dire su seek (http://perldoc.perl.org/functions/seek.html):

Annotare il in byte: anche se il filehandle è stato impostato per operare su caratteri (ad esempio usando il livello di codifica: encoding (utf8)), tell() restituirà offset di byte, non offset di caratteri (poiché l'implementazione renderebbe seek() e tell() piuttosto lento).

Quindi sembra che Tie :: File stia utilizzando le lunghezze dei caratteri per determinare i suoi offset di byte per i record. Pertanto può finire nel mezzo di una sequenza di caratteri UTF-8. Questa sembra una probabile causa per i tuoi errori.

In generale, mi allontano dallo binmode quando ci si affida a un modulo esterno per leggere/scrivere su un handle di file - in questo caso avrei una semplice sotto chiamata Encode::encode('UTF-8', ...) sui dati prima di passare a @tied.

Eccezione è dove la documentazione del modulo indica chiaramente il comportamento per i dati decodificati o se la fonte è abbastanza semplice per me per verificare il comportamento.

+1

grazie, questo è molto istruttivo, I deciso di contrassegnarlo accettato. Sebbene, ho finito per usare tie con DB_file e DBM_filter, come suggerito da remiah qui: http://perlmonks.org/?node_id=1002394. questo fa il lavoro. –