2013-04-03 6 views
6

Sto scrivendo un programma python che supporta tutte le maiuscole (una sostituzione per il non funzionante tr '[:lowers:]' '[:upper:]'). Le impostazioni internazionali sono ru_RU.UTF-8 e io uso PYTHONIOENCODING=UTF-8 per impostare le codifiche STDIN/STDOUT. Questo imposta correttamente sys.stdin.encoding. Quindi, perché devo ancora creare esplicitamente un wrapper di decodifica se sys.stdin conosce già la codifica? Se non si crea il lettore wrapping, la funzione .upper() non funziona correttamente (non fa nulla per i caratteri non ASCII).Perché devo fare `sys.stdin = codecs.getreader (sys.stdin.encoding) (sys.stdin)`?

import sys, codecs 
sys.stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin) #Why do I need this? 
for line in sys.stdin: 
    sys.stdout.write(line.upper()) 

Perché stdin ha .encoding se non usarlo?

+0

Quale versione di Python? prova 'line.decode (your_encoding) .upper()' – JBernardo

+0

Risposta breve: perché stai usando una versione obsoleta di Python piena di bagagli storici. – phihag

+0

@JBernardo Python versione è 2.7.3 (in FreeBSD 9) 'line.decode (sys.stdin.encoding) .upper()' funziona naturalmente. Ma la mia domanda è: perché abbiamo bisogno di tutto questo? –

risposta

10

Per rispondere a "perché", è necessario comprendere il tipo integrato file di Python 2.x, file.encoding e la loro relazione.

L'oggetto file integrato si occupa di byte non elaborati --- legge sempre e scrive byte non elaborati.

L'attributo encoding descrive la codifica dei byte grezzi nello stream. Questo attributo può o non può essere presente e potrebbe anche non essere affidabile (ad es. Abbiamo impostato PYTHONIOENCODING in modo non corretto nel caso di flussi standard).

L'unica volta che una conversione automatica viene eseguita dagli oggetti file si ha quando si scrive l'oggetto unicode su quel flusso. In tal caso, utilizzerà lo file.encoding se disponibile per eseguire la conversione.

In caso di lettura dei dati, l'oggetto file non esegue alcuna conversione poiché restituisce byte non elaborati. L'attributo encoding in questo caso è un suggerimento per l'utente di eseguire le conversioni manualmente.

file.encoding è impostato nel tuo caso, perché si imposta la variabile PYTHONIOENCODING e l'attributo 's il sys.stdinencoding è stato fissato di conseguenza. Per ottenere un flusso di testo dobbiamo avvolgerlo manualmente come hai fatto nel codice di esempio.

Per pensarci in un altro modo, immagina di non avere un tipo di testo separato (come Python 2.x's unicode o Python 3 str). Possiamo ancora lavorare con il testo usando i byte grezzi, ma tenendo traccia della codifica utilizzata. Questo è il modo in cui si intende utilizzare file.encoding (da utilizzare per tracciare la codifica). I wrapper di lettori che creiamo automaticamente eseguono il monitoraggio e le conversioni per noi.

Naturalmente, automaticamente avvolgendo sys.stdin sarebbe meglio (e questo è ciò Python 3.x fa), ma cambiando il comportamento predefinito di sys.stdin in Python 2.x si romperà compatibilità.

Quanto segue è un confronto di sys.stdin in Python 2.xe 3.x:

# Python 2.7.4 
>>> import sys 
>>> type(sys.stdin) 
<type 'file'> 
>>> sys.stdin.encoding 
'UTF-8' 
>>> w = sys.stdin.readline() 
## ... type stuff - enter 
>>> type(w) 
<type 'str'>   # In Python 2.x str is just raw bytes 
>>> import locale 
>>> locale.getdefaultlocale() 
('en_US', 'UTF-8') 

La io.TextIOWrapper class fa parte della libreria standard dal Python 2.6. Questa classe ha un attributo encoding che viene utilizzato per convertire i byte non elaborati in Unicode.

# Python 3.3.1 
>>> import sys 
>>> type(sys.stdin) 
<class '_io.TextIOWrapper'> 
>>> sys.stdin.encoding 
'UTF-8' 
>>> w = sys.stdin.readline() 
## ... type stuff - enter 
>>> type(w) 
<class 'str'>  # In Python 3.x str is Unicode 
>>> import locale 
>>> locale.getdefaultlocale() 
('en_US', 'UTF-8') 

La buffer attributo fornisce accesso al grezzo byte flusso appoggio stdin; questo di solito è un BufferedReader. Nota sotto che lo non ha un attributo encoding.

# Python 3.3.1 again 
>>> type(sys.stdin.buffer) 
<class '_io.BufferedReader'> 
>>> w = sys.stdin.buffer.readline() 
## ... type stuff - enter 
>>> type(w) 
<class 'bytes'>  # bytes is (kind of) equivalent to Python 2 str 
>>> sys.stdin.buffer.encoding 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: '_io.BufferedReader' object has no attribute 'encoding' 

In Python 3 la presenza o l'assenza dell'attributo encoding è coerente con il tipo di flusso usato.

+0

Grazie per la risposta. Ha confermato la mia convinzione che il design di 'Python 2' era cattivo. Gli oggetti (a meno che non siano semplici strutture di archiviazione dati) non dovrebbero contenere dati che non usano. La classe 'file' non dovrebbe usare la sua proprietà' .encoding' o rimuoverla. Questo difetto di progettazione è stato corretto in 'Python 3'. .Net lo gestisce allo stesso modo: ci sono 'Stream's basati su byte (puoi solo scrivere byte su di essi) e classi wrapper' TextReader'/'TextWriter'. È possibile utilizzare 'StreamReader.BaseStream' per accedere ai byte sottostanti. 'Console.In' è un' TextReader' (che riconosce la codifica). –

+0

Buono a sapersi che 'Python 3' sembra aver anche eliminato le stringhe non Unicode (sebbene ci siano ancora cose come' BufferedReader.readline() '). –

Problemi correlati