Perché non posso utilizzare la funzione map per creare un buon hash da un semplice file di dati in Perl?
-
21-09-2019 - |
Domanda
Il post è aggiornato.Passa gentilmente alla parte della soluzione, se hai già letto la domanda pubblicata.Grazie!
Ecco il codice ridotto a icona per mostrare il mio problema:
Il file di dati di input per il test è stato salvato dal Blocco note integrato di Windows come codifica UTF-8.Ha le seguenti tre righe:
abacus æbәkәs abalone æbәlәuni abandon әbændәn
Il file di script Perl è stato anche salvato dal Blocco note integrato di Windows come codifica UTF-8.Contiene il seguente codice:
#!perl -w
use Data::Dumper;
use strict;
use autodie;
open my $in,'<',"./hash_test.txt";
open my $out,'>',"./hash_result.txt";
my %hash = map {split/\t/,$_,2} <$in>;
print $out Dumper(\%hash),"\n";
print $out "$hash{abacus}";
print $out "$hash{abalone}";
print $out "$hash{abandon}";
Nell'output, la tabella hash sembra essere a posto:
$VAR1 = { 'abalone' => 'æbәlәuni ', 'abandon' => 'әbændәn', 'abacus' => 'æbәkәs ' };
Ma in realtà non lo è, perché ottengo solo due valori invece di tre:
æbәlәuni әbændәn
Perl fornisce il seguente messaggio di avviso:
Use of uninitialized value $hash{"abacus"} in string at C:\test2.pl line 11, <$i
n> line 3.
dov'è il problema?Qualcuno può gentilmente spiegare?Grazie.
La soluzione
Milioni di ringraziamenti a tutti voi ragazzi :) Ora finalmente viene trovato il colpevole e il problema diventa risolvibile :) Come @sinan in modo approfondito, ora sono sicuro al 100% che il colpevole per aver causato il problema che ho descritto sopra è i due byte di bom, che Notepad ha aggiunto al mio file di dati quando è stato salvato come UTF-8 e che in qualche modo Perl non tratta correttamente.Sebbene molti suggeriscano di usare "<:utf8" e ">:utf8" per leggere e scrivere file, il fatto è che queste configurazioni utf-8 non risolvono il problema.Invece potrebbero causare altri problemi.
Per risolvere davvero il problema, tutto ciò di cui ho bisogno è aggiungere una riga di codice per forzare Perl a ignorare la distinta base:
#!perl -w
use Data::Dumper;
use strict;
use autodie;
open my $in,'<',"./hash_test.txt";
open my $out,'>',"./hash_result.txt";
seek $in,3,0; # force Perl to ignore the BOM!
my %hash = map {split/\t/,$_,2} <$in>;
print $out Dumper(\%hash);
print $out $hash{abacus};
print $out $hash{abalone};
print $out $hash{abandon};
Ora, l'output è esattamente quello che mi aspettavo:
$VAR1 = { 'abalone' => 'æbәlәuni ', 'abandon' => 'әbændәn', 'abacus' => 'æbәkәs ' }; æbәkәs æbәlәuni әbændәn
Tieni presente che lo script viene salvato come codifica UTF-8 e il codice non deve includere etichette utf-8 perché il file di input e il file di output sono entrambi pre-salvati come codifica UTF-8.
Infine grazie ancora a tutti voi.E grazie, @Sinan, per la guida approfondita.Senza il tuo aiuto, rimarrei all'oscuro per Dio solo sa quanto tempo.
NotaPer chiarire un po' di più, se utilizzo:
open my $in,'<:utf8',"./hash_test.txt";
open my $out,'>:utf8',"./hash_result.txt";
my %hash = map {split/\t/,$_,2} <$in>;
print $out Dumper(\%hash);
print $out $hash{abacus};
print $out $hash{abalone};
print $out $hash{abandon};
L'output è questo:
$VAR1 = { 'abalone' => "\x{e6}b\x{4d9}l\x{4d9}uni ", 'abandon' => "\x{4d9}b\x{e6}nd\x{4d9}n", "\x{feff}abacus" => "\x{e6}b\x{4d9}k\x{4d9}s " }; æbәlәuni әbændәn
E il messaggio di avviso:
Use of uninitialized value in print at C:\hash_test.pl line 13, line 3.
Soluzione
Trovo il messaggio di avviso un po' sospetto.Ti dice che il $in
filehandle è alla riga 3 quando dovrebbe essere alla riga 4 dopo aver letto l'ultima riga.
Quando ho provato il tuo codice, ho salvato il file di input utilizzando GVim che è configurato sul mio sistema per il salvataggio come UTF-8, non ho riscontrato il problema.Ora che l'ho provato con Blocco note, guardando il file di output, vedo:
"\x{feff}abacus" => "\x{e6}b\x{4d9}k\x{4d9}s "
Dove \x{feff}
è il Distinta base.
Nell'output di Dumper, prima sono presenti spazi vuoti spuri abacus
(dove non avevi specificato :utf8
per la maniglia di uscita).
Come avevo accennato in origine (perso a causa delle innumerevoli modifiche a questo post - grazie per il promemoria hobbs), specifica '<:utf8'
quando apri il file di input.
Altri suggerimenti
Se vuoi leggere/scrivere file UTF8, dovresti assicurarti di leggerli effettivamente come UTF8.
#! /usr/bin/env perl
use Data::Dumper;
open my $in, '<:utf8', "hash_test.txt";
open my $out, '>:utf8', "hash_result.txt";
my %hash = map { chomp; split ' ', $_, 2 } <$in>;
print $out Dumper(\%hash),"\n";
print $out "$hash{abacus}\n";
print $out "$hash{abalone}\n";
print $out "$hash{abandon}\n";
Se vuoi che sia più robusto, si consiglia di usarlo :encoding(utf8)
invece di :utf8
, per leggere un file.
open my $in, '<:encoding(utf8)', "hash_test.txt";
Leggere PerlIO per maggiori informazioni.
Penso che la tua risposta potrebbe essere proprio di fronte a te.L'uscita da Data::Dumper
che hai postato è:
$VAR1 = {
'abalone' => 'æbәlәuni
',
'abandon' => 'әbændәn',
'abacus' => 'æbәkәs
'
};
Notare il carattere tra i '
E abacus
?Hai provato ad accedere al terzo valore tramite $hash{abacus}
.Questo non è corretto a causa di quel personaggio prima abacus
nel Dumper()
hash.Potresti provare a collegarlo a un ciclo che dovrebbe occuparsene:
foreach my $k (keys %hash) {
print $out $hash{$k};
}
dividere/\s/ invece di dividere/ /
Per me va bene.Sei sicuro che il tuo esempio corrisponda al codice e ai dati effettivi?