Esistono due fasi per l'elaborazione del testo Unicode. Il primo è "come posso inserirlo ed emetterlo senza perdere informazioni". Il secondo è "come tratto il testo secondo le convenzioni della lingua locale".
Il post di tchrist copre entrambi, ma la seconda parte è da dove proviene il 99% del testo nel suo post. La maggior parte dei programmi non gestisce nemmeno correttamente l'I / O, quindi è importante capire che prima ancora di iniziare a preoccuparsi della normalizzazione e delle regole di confronto.
Questo post mira a risolvere quel primo problema
Quando leggi i dati in Perl, non importa quale codifica sia. Alloca un po 'di memoria e ripone i byte lì. Se diciprint $str
, cancella semplicemente quei byte sul tuo terminale, che probabilmente è impostato per assumere che tutto ciò che è scritto su di esso sia UTF-8, e il tuo testo appare.
Meravigliosa.
Tranne che non lo è. Se provi a trattare i dati come testo, noterai che sta succedendo qualcosa di brutto. Non devi fare altro che length
vedere che ciò che Perl pensa della tua stringa e che cosa pensi della tua stringa non sono d'accordo. Scrivi una riga come: perl -E 'while(<>){ chomp; say length }'
e digita 文字化け
e otterrai 12 ... non la risposta corretta, 4.
Questo perché Perl presume che la tua stringa non sia testo. Devi dirgli che è un testo prima che ti dia la risposta giusta.
È abbastanza facile; il modulo Encode ha le funzioni per farlo. Il punto di ingresso generico è Encode::decode
(ouse Encode qw(decode)
, ovviamente). Quella funzione prende una stringa dal mondo esterno (ciò che chiameremo "ottetti", un modo stravagante di dire "byte a 8 bit"), e la trasforma in un testo che Perl capirà. Il primo argomento è un nome di codifica dei caratteri, come "UTF-8" o "ASCII" o "EUC-JP". Il secondo argomento è la stringa. Il valore restituito è lo scalare Perl contenente il testo.
(C'è anche Encode::decode_utf8
, che presuppone UTF-8 per la codifica.)
Se riscriviamo il nostro one-liner:
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
Digitiamo 文字 化 け e otteniamo "4" come risultato. Successo.
Questa, proprio lì, è la soluzione al 99% dei problemi Unicode in Perl.
La chiave è, ogni volta che un testo entra nel tuo programma, devi decodificarlo. Internet non può trasmettere caratteri. I file non possono memorizzare caratteri. Non ci sono caratteri nel tuo database. Ci sono solo ottetti e non puoi trattare gli ottetti come personaggi in Perl. È necessario decodificare gli ottetti codificati in caratteri Perl con il modulo Encode.
L'altra metà del problema è ottenere dati dal tuo programma. È facile; dite semplicemente use Encode qw(encode)
, decidete in che codifica saranno i vostri dati (UTF-8 ai terminali che comprendono UTF-8, UTF-16 per i file su Windows, ecc.), e quindi emettete il risultato encode($encoding, $data)
invece di emetterlo $data
.
Questa operazione converte i personaggi di Perl, su cui opera il tuo programma, in ottetti che possono essere utilizzati dal mondo esterno. Sarebbe molto più semplice se potessimo semplicemente inviare caratteri su Internet o ai nostri terminali, ma non possiamo: solo ottetti. Quindi dobbiamo convertire i caratteri in ottetti, altrimenti i risultati non sono definiti.
Riassumendo: codificare tutti gli output e decodificare tutti gli input.
Ora parleremo di tre problemi che lo rendono un po 'impegnativo. Il primo sono le biblioteche. Gestiscono il testo correttamente? La risposta è ... ci provano. Se scarichi una pagina Web, LWP ti restituirà il risultato come testo. Se si chiama il metodo giusto sul risultato, questo è (e quello sembra essere decoded_content
, non content
, che è solo il flusso di ottetti che ha ottenuto dal server.) I driver del database possono essere traballanti; se usi DBD :: SQLite con solo Perl, funzionerà, ma se qualche altro strumento ha messo il testo memorizzato come codifica diversa da UTF-8 nel tuo database ... beh ... non verrà gestito correttamente fino a quando non scrivi il codice per gestirlo correttamente.
Generare i dati di solito è più semplice, ma se vedi "carattere largo nella stampa", sai che stai incasinando la codifica da qualche parte. Quell'avvertimento significa "hey, stai cercando di far trapelare personaggi Perl nel mondo esterno e non ha alcun senso". Il tuo programma sembra funzionare (perché l'altra estremità di solito gestisce correttamente i caratteri Perl non elaborati), ma è molto rotto e potrebbe smettere di funzionare in qualsiasi momento. Risolvilo con un esplicito Encode::encode
!
Il secondo problema è il codice sorgente codificato UTF-8. A meno che tu non dica use utf8
all'inizio di ogni file, Perl non supporrà che il tuo codice sorgente sia UTF-8. Ciò significa che ogni volta che dici qualcosa del genere my $var = 'ほげ'
, stai iniettando immondizia nel tuo programma che distruggerà tutto in modo orribile. Non devi "usare utf8", ma se non lo fai, non devi usare caratteri non ASCII nel tuo programma.
Il terzo problema è come Perl gestisce il passato. Molto tempo fa, non esisteva nulla come Unicode e Perl supponeva che tutto fosse in latino-1 testo o binario. Quindi, quando i dati entrano nel tuo programma e inizi a trattarli come testo, Perl tratta ogni ottetto come un carattere Latino-1. Ecco perché, quando abbiamo chiesto la lunghezza di "文字 化 け", abbiamo ottenuto 12. Perl ha ipotizzato che stessimo operando sulla stringa Latin-1 "æååã" (che è di 12 caratteri, alcuni dei quali non stampabili).
Questo si chiama "aggiornamento implicito", ed è una cosa perfettamente ragionevole da fare, ma non è quello che vuoi se il tuo testo non è Latin-1. Ecco perché è fondamentale decodificare esplicitamente l'input: se non lo fai, Perl lo farà e potrebbe sbagliare.
Le persone si imbattono in problemi in cui metà dei loro dati è una stringa di caratteri corretta e alcuni sono ancora binari. Perl interpreterà la parte che è ancora binaria come se fosse un testo in latino-1 e poi la combinerà con i dati dei caratteri corretti. Questo farà sembrare che gestire i tuoi personaggi abbia rotto correttamente il tuo programma, ma in realtà non l'hai risolto abbastanza.
Ecco un esempio: hai un programma che legge un file di testo con codifica UTF-8, ti imbatti in un Unicode PILE OF POO
su ogni riga e lo stampi. Lo scrivi come:
while(<>){
chomp;
say "$_ 💩";
}
E quindi esegui alcuni dati codificati UTF-8, come:
perl poo.pl input-data.txt
Stampa i dati UTF-8 con una cacca alla fine di ogni riga. Perfetto, il mio programma funziona!
Ma no, stai solo facendo una concatenazione binaria. Stai leggendo ottetti dal file, rimuovendo a \n
con chomp e quindi virando sui byte nella rappresentazione UTF-8 del PILE OF POO
personaggio. Quando rivedi il tuo programma per decodificare i dati dal file e codificare l'output, noterai che ottieni spazzatura ("ð ©") invece della cacca. Questo ti porterà a credere che la decodifica del file di input sia la cosa sbagliata da fare. Non è.
Il problema è che la cacca viene implicitamente aggiornata come latin-1. Se devi use utf8
rendere il testo letterale anziché binario, allora funzionerà di nuovo!
(Questo è il problema numero uno che vedo quando aiuto le persone con Unicode. Hanno fatto la parte giusta e hanno interrotto il loro programma. Questo è ciò che è triste per i risultati indefiniti: puoi avere un programma di lavoro per molto tempo, ma quando inizi a ripararlo, non si preoccupa, se stai aggiungendo istruzioni di codifica / decodifica al tuo programma e si interrompe, significa solo che hai più lavoro da fare. La prossima volta, quando progetti con Unicode in mente dall'inizio, sarà molto più facile!)
Questo è davvero tutto ciò che devi sapere su Perl e Unicode. Se dici a Perl quali sono i tuoi dati, ha il miglior supporto Unicode tra tutti i linguaggi di programmazione più diffusi. Se supponi che saprà magicamente che tipo di testo lo stai alimentando, però, eliminerai i tuoi dati in modo irrevocabile. Solo perché il tuo programma funziona oggi sul tuo terminale UTF-8 non significa che funzionerà domani su un file codificato UTF-16. Quindi rendilo sicuro ora e risparmia il mal di testa di cestinare i dati dei tuoi utenti!
La parte facile della gestione di Unicode è la codifica dell'output e della decodifica dell'input. La parte difficile è trovare tutti i tuoi input e output e determinare quale codifica è. Ma è per questo che ottieni un sacco di soldi :)