2009-06-08 13 views
8

So che sto avendo un problema con una conversione da Unicode ma non sono sicuro di dove stia accadendo.Problema Unicode con SQLAlchemy

Sto estraendo dati su un recente viaggio Eruopeo da una directory di file HTML. Alcuni nomi di località hanno caratteri non ASCII (come é, ô, ü). Sto ottenendo i dati da una rappresentazione di stringa del file usando regex.

se stampo i luoghi come li trovo, stampano con i personaggi in modo che il codifica deve essere ok:

Le Pré-Saint-Gervais, France 
Hôtel-de-Ville, France 

sto memorizzare i dati in una tabella SQLite usando SQLAlchemy:

Base = declarative_base() 
class Point(Base): 
    __tablename__ = 'points' 

    id = Column(Integer, primary_key=True) 
    pdate = Column(Date) 
    ptime = Column(Time) 
    location = Column(Unicode(32)) 
    weather = Column(String(16)) 
    high = Column(Float) 
    low = Column(Float) 
    lat = Column(String(16)) 
    lon = Column(String(16)) 
    image = Column(String(64)) 
    caption = Column(String(64)) 

    def __init__(self, filename, pdate, ptime, location, weather, high, low, lat, lon, image, caption): 
     self.filename = filename 
     self.pdate = pdate 
     self.ptime = ptime 
     self.location = location 
     self.weather = weather 
     self.high = high 
     self.low = low 
     self.lat = lat 
     self.lon = lon 
     self.image = image 
     self.caption = caption 

    def __repr__(self): 
     return "<Point('%s','%s','%s')>" % (self.filename, self.pdate, self.ptime) 

engine = create_engine('sqlite:///:memory:', echo=False) 
Base.metadata.create_all(engine) 
Session = sessionmaker(bind = engine) 
session = Session() 

ciclo i attraverso i file e inserire i dati da ciascuno di essi nel database:

for filename in filelist: 

    # open the file and extract the information using regex such as: 
    location_re = re.compile("<h2>(.*)</h2>",re.M) 
    # extract other data 

    newpoint = Point(filename, pdate, ptime, location, weather, high, low, lat, lon, image, caption) 
    session.add(newpoint) 
    session.commit() 

Vedo il seguente avviso su ogni inserto:

/usr/lib/python2.5/site-packages/SQLAlchemy-0.5.4p2-py2.5.egg/sqlalchemy/engine/default.py:230: SAWarning: Unicode type received non-unicode bind param value 'Spitalfields, United Kingdom' 
    param.append(processors[key](compiled_params[key])) 

E quando cerco di fare qualsiasi cosa con il tavolo come ad esempio:

session.query(Point).all() 

ottengo:

Traceback (most recent call last): 
    File "./extract_trips.py", line 131, in <module> 
    session.query(Point).all() 
    File "/usr/lib/python2.5/site-packages/SQLAlchemy-0.5.4p2-py2.5.egg/sqlalchemy/orm/query.py", line 1193, in all 
    return list(self) 
    File "/usr/lib/python2.5/site-packages/SQLAlchemy-0.5.4p2-py2.5.egg/sqlalchemy/orm/query.py", line 1341, in instances 
    fetch = cursor.fetchall() 
    File "/usr/lib/python2.5/site-packages/SQLAlchemy-0.5.4p2-py2.5.egg/sqlalchemy/engine/base.py", line 1642, in fetchall 
    self.connection._handle_dbapi_exception(e, None, None, self.cursor, self.context) 
    File "/usr/lib/python2.5/site-packages/SQLAlchemy-0.5.4p2-py2.5.egg/sqlalchemy/engine/base.py", line 931, in _handle_dbapi_exception 
    raise exc.DBAPIError.instance(statement, parameters, e, connection_invalidated=is_disconnect) 
sqlalchemy.exc.OperationalError: (OperationalError) Could not decode to UTF-8 column 'points_location' with text 'Le Pré-Saint-Gervais, France' None None 

Vorrei essere in grado di memorizzare correttamente e quindi restituire i nomi delle posizioni con i caratteri originali intatti. Qualsiasi aiuto sarebbe molto apprezzato.

risposta

11

Ho trovato questo articolo che ha contribuito a spiegare i miei problemi un po ':

http://www.amk.ca/python/howto/unicode#reading-and-writing-unicode-data

sono stato in grado di ottenere i risultati desiderati, utilizzando il modulo 'codec' e poi cambiare il mio programma come segue:

Quando si apre il file:

infile = codecs.open(filename, 'r', encoding='iso-8859-1') 

Quando si stampa la posizione:

print location.encode('ISO-8859-1') 

ora posso interrogare e manipolare i dati della tabella, senza l'errore da prima. Devo solo specificare la codifica quando invio il testo.

(io ancora non del tutto a capire come questo sta funzionando quindi credo che è il momento di saperne di più sulla gestione unicode di Python ...)

+1

Vorrei provare "cp1252" prima di "iso-8859-1". E non so se il seguente aiuto aiuta a tutti: http://stackoverflow.com/questions/368805/python-unicodedecodeerror-am-i-misunderstanding-encode/370199#370199 – tzot

7

Provare a utilizzare un tipo di colonna di Unicode invece di String per le colonne unicode:

Base = declarative_base() 
class Point(Base): 
    __tablename__ = 'points' 

    id = Column(Integer, primary_key=True) 
    pdate = Column(Date) 
    ptime = Column(Time) 
    location = Column(Unicode(32)) 
    weather = Column(String(16)) 
    high = Column(Float) 
    low = Column(Float) 
    lat = Column(String(16)) 
    lon = Column(String(16)) 
    image = Column(String(64)) 
    caption = Column(String(64)) 

Edit: Risposta al commento:

Se stai ricevendo avvertimenti circa codifiche Unicode poi ci sono due cose puoi provare:

  1. Converti la tua posizione in unicode. Ciò significherebbe avere il vostro punto creato in questo modo:

    Newpoint = Point (nome del file, pdate, ptime, unicode (posizione), tempo, alto, basso, lat, lon, immagine, didascalia)

    La conversione unicode produrrà una stringa unicode quando passerai una stringa o una stringa unicode, quindi non dovrai preoccuparti di ciò che passi.

  2. Se ciò non risolve i problemi di codifica, prova a chiamare la codifica sul tuo unicode oggetti. Ciò significherebbe usare codice come:

    newpoint = Punto (nomefile, pdate, ptime, unicode (posizione) .encode ('utf-8'), meteo, alto, basso, lat, lon, immagine, didascalia)

    Questo passaggio probabilmente non sarà necessario, ma ciò che essenzialmente fa è convertire un oggetto unicode da code-point unicode a una specifica rappresentazione di byte (in questo caso, utf-8). Mi aspetterei che SQLAlchemy faccia questo per te quando passi in oggetti unicode ma potrebbe non farlo.

+0

Grazie per il suggerimento. Penso che questo mi stia andando nella giusta direzione. Ora sto ricevendo avvisi circa la codifica dei dati che sto inserendo, ma non sono sicuro su come risolvere questo problema. Ho aggiornato la mia domanda per riflettere il tuo suggerimento. –

7

Da sqlalchemy.org

Vedere la sezione 0.4.2

aggiunto nuova bandiera per String e create_engine(), assert _UNICODE = (true | false | 'avvertire' | Nessuno) . Per default su False o None su creare _engine() e String, 'warn' sul tipo Unicode. Quando True, genera tutte le operazioni di conversione unicode che generano un'eccezione quando un parametro non unicode da viene passato come parametro di collegamento. 'warn' risultati in un avviso. Si consiglia vivamente che tutte le applicazioni compatibili con Unicode facciano uso appropriato degli oggetti unicode Python (ad esempio "ciao" e non "ciao") in modo che i dati sui round trip siano accurati.

Penso che si stia tentando di inserire un test non Unicode. Forse questo potrebbe portarti sulla strada giusta? È necessaria una qualche forma di conversione, confrontare 'ciao' e u'hello '.

Acclamazioni