L'intera chiave di questi problemi di codifica è capire che in linea di principio ci sono due concetti distinti di "stringa" : (1) stringa di caratteri e (2) stringa / array di byte. Questa distinzione è stata per lo più ignorata per molto tempo a causa dell'ubiquità storica delle codifiche con non più di 256 caratteri (ASCII, Latin-1, Windows-1252, Mac OS Roman, ...): queste codifiche mappano un insieme di caratteri comuni a numeri compresi tra 0 e 255 (cioè byte); lo scambio relativamente limitato di file prima dell'avvento del web rendeva tollerabile questa situazione di codifiche incompatibili, poiché la maggior parte dei programmi poteva ignorare il fatto che c'erano più codifiche fintanto che producevano testo che rimaneva sullo stesso sistema operativo: tali programmi semplicemente tratta il testo come byte (tramite la codifica utilizzata dal sistema operativo). La visualizzazione corretta e moderna separa correttamente questi due concetti di stringa, in base ai seguenti due punti:
I caratteri sono per lo più estranei ai computer : è possibile disegnarli su una lavagna, ecc., Come ad esempio بايثون, 中 蟒 e 🐍. I "caratteri" per le macchine includono anche "istruzioni di disegno" come ad esempio spazi, ritorno a capo, istruzioni per impostare la direzione di scrittura (per l'arabo, ecc.), Accenti, ecc. Un elenco di caratteri molto grande è incluso nello standard Unicode ; copre la maggior parte dei personaggi conosciuti.
D'altra parte, i computer hanno bisogno di rappresentare caratteri astratti in qualche modo: per questo, usano array di byte (numeri compresi tra 0 e 255 inclusi), perché la loro memoria è composta da blocchi di byte. Il processo necessario che converte i caratteri in byte è chiamato codifica . Pertanto, un computer richiede una codifica per rappresentare i caratteri. Qualsiasi testo presente sul tuo computer viene codificato (fino a quando non viene visualizzato), sia che venga inviato a un terminale (che si aspetta caratteri codificati in un modo specifico), sia che venga salvato in un file. Per essere visualizzati o "compresi" correttamente (ad esempio, dall'interprete Python), i flussi di byte vengono decodificati in caratteri. Alcune codifiche(UTF-8, UTF-16, ...) sono definiti da Unicode per il suo elenco di caratteri (Unicode quindi definisce sia un elenco di caratteri sia codifiche per questi caratteri - ci sono ancora punti in cui si vede l'espressione "Codifica Unicode" come un modo per fare riferimento all'onnipresente UTF-8, ma questa è una terminologia errata, poiché Unicode fornisce più codifiche).
In sintesi, i computer devono rappresentare internamente i caratteri con byte e lo fanno attraverso due operazioni:
Codifica : caratteri → byte
Decodifica : byte → caratteri
Alcune codifiche non possono codificare tutti i caratteri (es. ASCII), mentre (alcune) codifiche Unicode consentono di codificare tutti i caratteri Unicode. Anche la codifica non è necessariamente univoca , perché alcuni caratteri possono essere rappresentati direttamente o come combinazione (ad esempio di un carattere di base e di accenti).
Si noti che il concetto di nuova riga aggiunge uno strato di complicazione , poiché può essere rappresentato da diversi caratteri (di controllo) che dipendono dal sistema operativo (questo è il motivo per la modalità di lettura dei file di nuova riga universale di Python ).
Quello che ho chiamato "carattere" sopra è quello che Unicode chiama un " carattere percepito dall'utente ". Un singolo carattere percepito dall'utente a volte può essere rappresentato in Unicode combinando parti di caratteri (carattere di base, accenti, ...) trovati in diversi indici nell'elenco Unicode, che sono chiamati " punti di codice " - questi punti di codice possono essere combinati insieme per formare un "grapheme cluster". Unicode porta così a un terzo concetto di stringa, costituito da una sequenza di punti di codice Unicode, che si trova tra byte e stringhe di caratteri, e che è più vicino a quest'ultima. Li chiamerò " stringhe Unicode " (come in Python 2).
Mentre Python può stampare stringhe di caratteri (percepiti dall'utente), le stringhe non byte Python sono essenzialmente sequenze di punti di codice Unicode , non di caratteri percepiti dall'utente. I valori del punto di codice sono quelli usati nella sintassi delle stringhe di Python \u
e \U
Unicode. Non devono essere confusi con la codifica di un carattere (e non devono avere alcuna relazione con esso: i punti di codice Unicode possono essere codificati in vari modi).
Ciò ha una conseguenza importante: la lunghezza di una stringa Python (Unicode) è il suo numero di punti di codice, che non è sempre il suo numero di caratteri percepiti dall'utente : quindi s = "\u1100\u1161\u11a8"; print(s, "len", len(s))
(Python 3) dà 각 len 3
nonostante s
abbia un unico utente percepito (coreano) carattere (perché è rappresentato con 3 punti di codice, anche se non è necessario, come print("\uac01")
mostra). Tuttavia, in molte circostanze pratiche, la lunghezza di una stringa è il suo numero di caratteri percepiti dall'utente, perché molti caratteri sono tipicamente memorizzati da Python come un singolo punto di codice Unicode.
In Python 2 , le stringhe Unicode sono chiamate… "stringhe Unicode" ( unicode
tipo, forma letterale u"…"
), mentre gli array di byte sono "stringhe" ( str
tipo, dove l'array di byte può essere costruito per esempio con stringhe letterali "…"
). In Python 3 , le stringhe Unicode sono semplicemente chiamate "stringhe" ( str
tipo, forma letterale "…"
), mentre gli array di byte sono "byte" ( bytes
tipo, forma letterale b"…"
). Di conseguenza, qualcosa di simile "🐍"[0]
dà un risultato diverso in Python 2 ( '\xf0'
, un byte) e Python 3 ( "🐍"
, il primo e unico carattere).
Con questi pochi punti chiave, dovresti essere in grado di comprendere la maggior parte delle domande relative alla codifica!
Normalmente, quando stampi u"…"
su un terminale , non dovresti ottenere spazzatura: Python conosce la codifica del tuo terminale. In effetti, puoi verificare cosa si aspetta dalla codifica il terminale:
% python
Python 2.7.6 (default, Nov 15 2013, 15:20:37)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print sys.stdout.encoding
UTF-8
Se i tuoi caratteri di input possono essere codificati con la codifica del terminale, Python lo farà e invierà i byte corrispondenti al tuo terminale senza lamentarsi. Il terminale farà quindi del suo meglio per visualizzare i caratteri dopo aver decodificato i byte di input (nel peggiore dei casi il carattere del terminale non ha alcuni caratteri e stamperà invece una sorta di vuoto).
Se i caratteri immessi non possono essere codificati con la codifica del terminale, significa che il terminale non è configurato per visualizzare questi caratteri. Python si lamenterà (in Python con un UnicodeEncodeError
poiché la stringa di caratteri non può essere codificata in un modo che si adatta al tuo terminale). L'unica soluzione possibile è utilizzare un terminale in grado di visualizzare i caratteri (sia configurando il terminale in modo che accetti una codifica che possa rappresentare i vostri caratteri, sia utilizzando un programma terminale diverso). Questo è importante quando si distribuiscono programmi che possono essere utilizzati in ambienti diversi: i messaggi che si stampano dovrebbero essere rappresentabili nel terminale dell'utente. A volte è quindi meglio attenersi a stringhe che contengono solo caratteri ASCII.
Tuttavia, quando si reindirizza o si reindirizza l'output del programma, in genere non è possibile sapere quale sia la codifica di input del programma ricevente e il codice sopra restituisce una codifica predefinita: Nessuno (Python 2.7) o UTF-8 ( Python 3):
% python2.7 -c "import sys; print sys.stdout.encoding" | cat
None
% python3.4 -c "import sys; print(sys.stdout.encoding)" | cat
UTF-8
La codifica di stdin, stdout e stderr può comunque essere impostata tramite la PYTHONIOENCODING
variabile d'ambiente, se necessario:
% PYTHONIOENCODING=UTF-8 python2.7 -c "import sys; print sys.stdout.encoding" | cat
UTF-8
Se la stampa su un terminale non produce ciò che ti aspetti, puoi controllare che la codifica UTF-8 inserita manualmente sia corretta; per esempio, il tuo primo carattere ( \u001A
) non è stampabile, se non sbaglio .
Su http://wiki.python.org/moin/PrintFails , puoi trovare una soluzione come la seguente, per Python 2.x:
import codecs
import locale
import sys
# Wrap sys.stdout into a StreamWriter to allow writing unicode.
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)
uni = u"\u001A\u0BC3\u1451\U0001D10C"
print uni
Per Python 3, puoi controllare una delle domande poste in precedenza su StackOverflow.