SQLAlchemy: работа с данными CP-1252, когда Python ожидает, что он будет UTF-8
Я работаю с существующей базой данных SQLite и испытываю ошибки из-за данных, которые кодируются в CP-1252, когда Python ожидает, что он будет UTF-8.
>>> import sqlite3
>>> conn = sqlite3.connect('dnd.sqlite')
>>> curs = conn.cursor()
>>> result = curs.execute("SELECT * FROM dnd_characterclass WHERE id=802")
Traceback (most recent call last):
File "<input>", line 1, in <module>
OperationalError: Could not decode to UTF-8 column 'short_description_html'
with text ' <p>Over a dozen deities have worshipers who are paladins,
promoting law and good across Faer�n, but it is the Weave itself that
Оскорбительный символ \0xfb
, который декодирует до û
. Другие тексты-нарушители включают "?nd and slay illithids."
, который использует "умные кавычки" \0x93
и \0x94
.
SQLite, python, unicode и не-utf данные подробно описывают, как эта проблема может быть решена при использовании sqlite3
самостоятельно.
Тем не менее, я использую SQLAlchemy. Как я могу обрабатывать закодированные данные CP-1252 в базе данных SQLite, когда я использую SQLAlchemy?
Edit:
Это также применимо к любым другим смешным кодировкам в SQLite TEXT
, например, latin-1
, cp437
и т.д.
Ответы
Ответ 1
SQLAlchemy и SQLite ведут себя нормально. Решение состоит в том, чтобы исправить данные не UTF-8 в базе данных.
Я написал ниже, черпая вдохновение из fooobar.com/questions/36047/.... Это:
- загружает целевую базу данных SQLite
- перечисляет все столбцы во всех таблицах
- если столбец имеет тип
text
, char
или clob
- включая варианты, такие как varchar
и longtext
- он перекодирует данные из INPUT_ENCODING
в UTF-8.
INPUT_ENCODING = 'cp1252' # The encoding you want to convert from
import sqlite3
db = sqlite3.connect('dnd_fixed.sqlite')
db.create_function('FIXENCODING', 1, lambda s: str(s).decode(INPUT_ENCODING))
cur = db.cursor()
tables = cur.execute('SELECT name FROM sqlite_master WHERE type="table"').fetchall()
tables = [t[0] for t in tables]
for table in tables:
columns = cur.execute('PRAGMA table_info(%s)' % table ).fetchall() # Note: pragma arguments can't be parameterized.
for column_id, column_name, column_type, nullable, default_value, primary_key in columns:
if ('char' in column_type) or ('text' in column_type) or ('clob' in column_type):
# Table names and column names can't be parameterized either.
db.execute('UPDATE "{0}" SET "{1}" = FIXENCODING(CAST("{1}" AS BLOB))'.format(table, column_name))
После этого script все поля *text*
, *char*
и *clob*
находятся в UTF-8, и не будет больше ошибок декодирования Unicode. Теперь я могу Faerûn
выразить свое сердце.
Ответ 2
Если у вас есть URI соединения, вы можете добавить следующие параметры в URI для подключения к базе данных:
DB_CONNECTION = mysql+pymysql://{username}:{password}@{host}/{db_name}?{options}
DB_OPTIONS = {
"charset": "cp-1252",
"use_unicode": 1,
}
connection_uri = DB_CONNECTION.format(
username=???,
...,
options=urllib.urlencode(DB_OPTIONS)
)
Предполагая, что ваш драйвер SQLLite может обрабатывать эти параметры (pymysql может, но я не знаю 100% об sqllite), ваши запросы возвратят строки unicode.