Perché non posso utilizzare la funzione map per creare un buon hash da un semplice file di dati in Perl?

StackOverflow https://stackoverflow.com/questions/1762977

  •  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.
È stato utile?

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?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top