2009-07-20 10 views
5

Ho uno script Python 2.6 con caratteri speciali, codificato in Latin-1, che sto recuperando da un database SQL Server. Vorrei stampare questi caratteri, ma sono un po 'limitato perché sto usando una libreria che chiama lo stabilimento unicode e non so come fare in modo che Python usi un codec diverso da ascii.Latin-1 e la fabbrica unicode in Python

Lo script è uno strumento semplice per restituire i dati di ricerca da un database senza dover eseguire l'SQL direttamente in un editor SQL. Io uso la libreria PrettyTable 0.5 per visualizzare i risultati.

Il nucleo dello script è questo bit di codice. Le tuple che ottengo dal cursore contengono dati interi e string, e nessun dato Unicode. (Userei adodbapi invece di pyodbc, che mi otterrebbe Unicode, ma adodbapi mi dà altri problemi.)

x = pyodbc.connect(cxnstring) 
r = x.cursor() 
r.execute(sql) 

t = PrettyTable(columns) 
for rec in r: 
    t.add_row(rec) 
r.close() 
x.close() 

t.set_field_align("ID", 'r') 
t.set_field_align("Name", 'l') 
print t 

ma la colonna Name può contenere caratteri che non rientrano nel campo di ASCII. Io a volte un messaggio di errore come questo, in linea di 222 prettytable.pyc, quando si arriva alla chiamata t.add_row:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xed in position 12: ordinal not in range(128) 

Questa è la riga 222 in prettytable.py. Usa unicode, che è la fonte dei miei problemi, e non solo in questo script, ma in altri script Python che ho scritto.

for i in range(0,len(row)): 
    if len(unicode(row[i])) > self.widths[i]: # This is line 222 
     self.widths[i] = len(unicode(row[i])) 

Per favore dimmi cosa sto facendo male qui. Come posso fare in modo che unicode funzioni senza l'hacking prettytable.py o una delle altre librerie che uso? C'è anche un modo per farlo?

MODIFICA: l'errore non si verifica nell'istruzione print, ma nella chiamata t.add_row.

EDIT: Con l'aiuto di Bastien Léonard, ho trovato la seguente soluzione. Non è una panacea, ma funziona.

x = pyodbc.connect(cxnstring) 
r = x.cursor() 
r.execute(sql) 

t = PrettyTable(columns) 
for rec in r: 
    urec = [s.decode('latin-1') if isinstance(s, str) else s for s in rec] 
    t.add_row(urec) 
r.close() 
x.close() 

t.set_field_align("ID", 'r') 
t.set_field_align("Name", 'l') 
print t.get_string().encode('latin-1') 

Ho finito per dover decodificare all'ingresso e codificare sull'uscita. Tutto questo mi fa sperare che tutti portino le loro librerie su Python 3.x prima possibile!

risposta

5

Aggiungere questo all'inizio del modulo:

# coding: latin1 

o decodificare la stringa da Unicode da soli.

[Edit]

E 'stato un po' da quando ho giocato con Unicode, ma spero che questo esempio mostrerà come convertire da Latin1 a Unicode:

>>> s = u'ééé'.encode('latin1') # a string you may get from the database 
>>> s.decode('latin1') 
u'\xe9\xe9\xe9' 

[Edit]

Documentazione :
http://docs.python.org/howto/unicode.html
http://docs.python.org/library/codecs.html

+0

Ho provato a inserire la codifica nella parte superiore dei miei script, ma ciò non funziona ancora. Proverò la decodifica esplicita, ma spero che ci sia una soluzione più generale. – eksortso

+1

Probabilmente non si desidera impostare la codifica: latin1. Questo cambia la codifica della fonte dello script, non i suoi dati. –

+0

@Glenn: L'ho suggerito perché pensavo che 'print t' possa stampare stringhe raw Latin1. –

2

Forse provare a decodificare le stringhe con codifica latin1 in unicode?

t.add_row((value.decode('latin1') for value in rec)) 
+1

t.add_row ([s.decode ('latin-1') se isinstance (s, str) else s per s in rec]) # Penso che tu intendessi questo (o qualcosa del genere). – eksortso

+0

Probabilmente, a seconda di cosa abbia bisogno di quel qualcosa di grazioso. – liori

0

Dopo un breve sguardo alla fonte per Prettytable, sembra che funziona su oggetti Unicode internamente (vedi _stringify_row, add_row e add_column, per esempio). Poiché non sa quale codifica le stringhe di input stanno utilizzando, utilizza la codifica predefinita, usually ascii.

Ora ascii è un sottoinsieme di latin-1, il che significa che se si esegue la conversione da ascii a latin-1, non si dovrebbero avere problemi. Il contrario, tuttavia, non è vero; non tutti i caratteri latini-1 mappano ai caratteri ascii. Per dimostrare questo:

>>> s = u'\xed\x31\x32\x33' 
>>> print s 
# FAILS: Python calls "s.decode('ascii')", but ascii codec can't decode '\xed' 
>>> print s.decode('ascii') 
# FAILS: Same as above 
>>> print s.decode('latin-1') 
í123 

esplicitamente convertire le stringhe Unicode (come alla fine si fatto) fissa le cose, e rende più senso, IMO - è molto più probabile di sapere che cosa i vostri dati charset sta usando, rispetto al autore di PrettyTable :). BTW, è possibile omettere il controllo per le stringhe nella comprensione dell'elenco sostituendo s.decode('latin-1') con unicode(s, 'latin-1') poiché tutti gli oggetti possono essere forzati alle stringhe.

Un'ultima cosa: non dimenticare di controllare il set di caratteri del tuo database e tabelle - non vuoi assumere 'latin-1' nel codice, quando i dati vengono effettivamente memorizzati come qualcos'altro ('utf-8'?) nel database. In MySQL, è possibile utilizzare il comando SHOW CREATE TABLE <table_name> per scoprire quale set di caratteri viene utilizzato da una tabella e SHOW CREATE DATABASE <db_name> per fare lo stesso per un database.

+0

Se utilizza internamente oggetti Unicode, dovrebbe essere possibile ripristinare gli oggetti Unicode ed evitare inutilmente la conversione avanti e indietro. Finché usi sempre gli oggetti Unicode, eviti la maggior parte di questo disordine (ecco come funziona sempre Python 3). –

+0

Credo di sì: "get_string". – elo80ka

+0

@ elo80ka, i dati vengono effettivamente memorizzati come Latin-1. L'ho verificato prima di scrivere la sceneggiatura. Inoltre (in Python 2.6 almeno), gli ints non possono essere forzati usando 'unicode (int_value, 'latin-1')', anche se 'unicode (int_value)' funziona. @Glenn Maynard, la stampa dei risultati comporta una decodifica, definita esplicitamente o meno. Ho dovuto usare 't.get_string(). Encode ('latin-1')' Sì, non vedo l'ora dell'adozione diffusa di py3k, in modo che tutte le stringhe siano Unicode. Risparmierebbe un sacco di problemi. – eksortso