[Python] Encoding e setdefaultencoding

Daniele Varrazzo piro a develer.com
Mar 13 Nov 2007 19:26:55 CET


Cristian Re ha scritto:
> Ecco un esempio che non quadra.
>  
> Ora costruiamo la stinga se scrivo:
>  
> str = 'perché è così?'
>  
> da quel che ho capito ho creato una stringa codificata con codifica 
> microsoft "cp1252" giusto?
> per cui se, sempre da console, scrivo "str" e dò invio l'output è ascii 
> (codifica di default di python) ed il risultato è:
> 'perch\x82 \x8a cos\x8d?'

I problemi che stai avendo vengono in parte anche dalla console e la shell 
interattiva: dalla stringa che mi hai passato vedo che usi una console cp850. 
Questo è ciò che succede sulla mia macchina linux con console latin1:

     >>> 'perché è così'
     'perch\xe9 \xe8 cos\xec'

come vedi i codici delle lettere accentate sono diversi (puoi verificare su 
wikipedia alle pagine cp850 e cp1252 che gli encoding sono questi).

Quindi se vuoi creare un literal string unicode dalla tua console interattiva, 
devi fare:

     'perché è così'.decode('cp850')

Questo però solo in interattivo: prova a scrivere la stessa frase nel tuo 
editor di testo, salvarla e leggere il risultato con file('miofile').read().

Possono succedere un paio di cose diverse:

  - leggi 'perch\xe9 \xe8 cos\xec': il file è stato salvato in latin1 o in cp1252
  - leggi 'perch\xc3\xa9 \xc3\xa8 cos\xc3\xac': il file è stato salvato in utf8
  - (molto improbabile) leggi 'perch\x82 \x8a cos\x8d': cp850, ormai lo 
sappiamo, ma non conosco nessun editor che lo faccia di default (cp850 è roba 
DOS!)

Tutto molto ambiguo, vero? Lo è: infatti se tu il literal che hai scritto 
nell'interattivo provi a scriverlo in un file sorgente .py, l'interprete ti 
farà una partaccia nel momento in cui eseguirai il file:

     piro a kaa ~ $ echo 'a = "perché è così"' > foo.py
     piro a kaa ~ $ python foo.py
       File "foo.py", line 1
     SyntaxError: Non-ASCII character '\xe9' in file foo.py on line 1, but no 
encoding declared; see http://www.python.org/peps/pep-0263.html for details

Se un file sorgente Python non contiene solo caratteri ascii (con ord(c) <= 
127), allora devi specificare esplicitamente l'encoding che hai usato nel 
file, usando la sintassi suggerita nella url del cazziatone :)

>  
> mentre se scrivo "print str" la scritta viene visualizzata come quando 
> la digito perché l'output è microsoft per cui "cp1252" ok?

Questo perché sia in input che in output (meno male!) la console è cp850, 
quindi le due supposizioni sbagliate si annullano ("poggio e buca fa piano") :)

> Se invece volessi crearmi una stringa unicode dovrei utilizzare 
> "unicode" specificando che l'input è "cp1252" ma se faccio così:
>  
> "str=unicode('perché è così', 'cp1252')"
>  
> mi da questo errore:
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "C:\Python25\lib\encodings\cp1252.py", line 15, in decode
>     return codecs.charmap_decode (input,errors,decoding_table)
> UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 
> 12: character maps to <undefined>
>  
> mentre se scrivo "str=unicode('perché è così', 'latin-1')" non da 
> errore. Non ci sto capendo più nulla però.

Perché appunto il 12mo carattere (0x8d) non è un carattere cp1252 esistente 
(vedi tabella su wikipedia). Invece unicode('perché è così', 'cp850') dovrebbe 
funzionare. E' strano che 'latin1' non dia errore perché lo stesso carattere 
non è definito neanche in questo encoding.

> ora se ridigito da console "str" l'output sembra una stringa unicode:
> u'perch\x82 \x8a cos\x8d'
>  
> ma se scrivo print str da questo errore nel file "cp850":
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "C:\Python25\lib\encodings\cp850.py", line 12, in encode
>     return codecs.charmap_encode(input,errors,encoding_map)
> UnicodeEncodeError: 'charmap' codec can't encode character u'\x82' in 
> position 5: character maps to <undefined>

Infatti è una stringa unicode, ma siccome hai fatto il decode col codec 
sbagliato, i caratteri unicode creati non sono quelli che ti aspetti. E credo 
sfiga voglia che hai beccato esattamente dei caratteri inesistenti! (perché 
unicode coincide con latin1 per i primi 256 codepoint, quindi quelli da 0x79 a 
0x9F non sono definiti)

> Mi sono perso anche nella spiegazione, latin-1, cp1252, cp850... non so 
> più nemmeno cosa stavo chiedendo.
> Avete idea di qualche sito che spiega bene e semplicemente la codifica 
> con python?

Il problema non è strettamente Python: sono un macello le codifiche e basta. 
Un impiccio extra per te è la console interattiva, che probabilmente non ha lo 
stesso encoding dei file che crei con un editor windows qualunque.

I problemi cominciano a diminuire quando:

  - conosci l'encoding di input,
  - ottieni una stringa unicode facendo il decode() giusto
  - lavori con unicode
  - quando devi scrivere in un consumatore che consuma stream 8 bit,
    conosci l'encoding che desidera
  - fai l'encode() del tuo unicode come lo vuole il consumatore e glielo passi.

Dimmi se ti ho chiarito qualcosa o ti ho fatto venire voglia di darti 
all'apicultura :)

Per favore, se rispondi a questo messaggio, scrivi sotto alle frasi a cui vuoi 
rispondere, non sopra, altrimenti diventa difficile seguire il discorso ;)

Ciao!

-- 
Daniele Varrazzo - Develer S.r.l.
http://www.develer.com


Maggiori informazioni sulla lista Python