SQLite, Python, unicode, и non-utf данные

Я запустил путем попытки сохранить строки в sqlite использование Python и получил сообщение:

sqlite3. ProgrammingError: Вы не должны использовать 8-разрядные строки байтов, если Вы не используете text_factory, который может интерпретировать 8-разрядные строки байтов (как text_factory = ул.). Настоятельно рекомендовано, что Вы вместо этого просто переключаете свое приложение на строки Unicode.

Хорошо, я переключился на строки Unicode. Затем я начал получать сообщение:

sqlite3. OperationalError: не Мог декодировать к столбцу UTF-8 'tag_artist' с текстом 'Sigur Ros'

при попытке получить данные из дб. Больше исследования и я начали кодировать его в utf8, но затем 'Sigur Ros' начинают быть похожими 'на Rós' Sigur

примечание: Моя консоль была установлена отобразиться в 'latin_1' как @John Machin, на который указывают.

Что дает? После чтения этого описывая точно ту же ситуацию я нахожусь в, кажется, как будто совет состоит в том, чтобы проигнорировать другой совет и использовать 8-разрядные строки байтов, в конце концов.

Я не знал много о unicode и utf, прежде чем я запустил этот процесс. Я изучил вполне немного в прошлые часы пары, но я все еще незнаком о том, существует ли способ правильно преобразовать 'ó' от латинского 1 до utf-8 и не исказить его. Если бы нет, почему sqlite 'настоятельно рекомендовал' бы, чтобы я переключил свое приложение на строки unicode?


Я собираюсь обновить этот вопрос со сводкой и некоторым примером кода всего, что я изучил за прошлые 24 часа так, чтобы у кого-то в моей обуви могло быть легкое (er) руководство. Если информация, которую я размещаю, является неправильной или вводящей в заблуждение, всегда скажите мне, и я обновлю, или один из Вас, старшие парни могут обновить.


Сводка ответов

Позвольте мне сначала заявить цель насколько я понимаю. Цель в обработке различной кодировки, при попытке преобразовать между ними, состоит в том, чтобы понять, каково Ваше исходное кодирование, затем преобразуйте его в использование unicode что исходное кодирование, затем преобразуйте его в свое желаемое кодирование. Unicode является основой, и кодировка является отображениями подмножеств той основы. utf_8 имеет пространство для каждого символа в unicode, но потому что они не находятся в том же месте как, например, latin_1, строка, закодированная в utf_8 и отправленная в latin_1 консоль, не посмотрит способ, которым Вы ожидаете. В Python процесс получения к unicode и в другое кодирование похож:

str.decode('source_encoding').encode('desired_encoding')

или если ул. уже находится в unicode

str.encode('desired_encoding')

Для sqlite я на самом деле не хотел кодировать его снова, я хотел декодировать его и оставить его в unicode формате. Вот четыре вещи, о которых Вы, возможно, должны были бы знать, поскольку Вы пытаетесь работать с unicode и кодировкой в Python.

  1. Кодирование строки, Вы хотите работать с, и кодирование Вас, хочет получить его к.
  2. Системное кодирование.
  3. Консольное кодирование.
  4. Кодирование исходного файла

Разработка:

(1) При чтении строки из источника это должно иметь некоторое кодирование, как latin_1 или utf_8. В моем случае я получаю строки от имен файлов, поэтому к сожалению, я мог получать любой вид кодирования. Windows XP использует UCS-2 (система Unicode) как ее собственный строковый тип, который походит на обман мне. К счастью, для меня, символы в большинстве имен файлов не будут составленными больше чем из одного исходного типа кодировки, и я думаю, что все мои были или полностью latin_1, полностью utf_8, или просто ASCII (который является подмножеством обоих из тех). Таким образом, я просто считал их и декодировал их, как будто они были все еще в latin_1 или utf_8. Возможно, тем не менее, что у Вас могли быть latin_1 и utf_8 и независимо от того, что другие символы смешали в имени файла в Windows. Иногда те символы могут обнаружиться как поля, другие времена, они просто выглядят искаженными, и другие времена, они выглядят корректными (символы с диакритикой и этажерка). Хождение дальше.

(2) Python имеет систему по умолчанию, кодирующую, который установлен, когда Python запускается и не может быть изменен во время времени выполнения. Посмотрите здесь для деталей. Грязная сводка... хорошо вот является файлом, который я добавил:

\# sitecustomize.py  
\# this file can be anywhere in your Python path,  
\# but it usually goes in ${pythondir}/lib/site-packages/  
import sys  
sys.setdefaultencoding('utf_8')  

Это системное кодирование является тем, которое привыкает, когда Вы используете unicode ("ул.") функция без любых других параметров кодирования. Сказать, что иначе, Python пытается декодировать "ул." к unicode на основе системного кодирования по умолчанию.

(3) Если Вы используете НЕАКТИВНЫЙ или Python командной строки, я думаю, что Ваша консоль отобразится согласно системному кодированию по умолчанию. Я использую pydev с затмением по некоторым причинам, таким образом, я должен был войти в свои настройки проекта, отредактировать свойства конфигурации запуска моего сценария тестирования, перейдите к вкладке Common и измените консоль от латинского 1 до utf-8 так, чтобы я мог визуально подтвердить то, что я делал, работал.

(4) Если Вы хотите иметь некоторые тестовые строки, например,

test_str = "ó"

в Вашем исходном коде затем необходимо будет сказать Python, какое кодирование Вы используете в том файле. (К вашему сведению: когда я ввел кодирование с опечаткой, я имел к ctrl-Z, потому что мой файл стал нечитабельным.) Это легко выполняется путем помещения строки как так наверху файла исходного кода:

# -*- coding: utf_8 -*-

Если у Вас нет этой информации, Python пытается проанализировать Ваш код как ASCII по умолчанию, и таким образом:

SyntaxError: Non-ASCII character '\xf3' in file _redacted_ on line 81, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

После того как Ваша программа работает правильно, или, если Вы не будете использовать консоль Python или какую-либо другую консоль для рассмотрения вывода, то затем Вы будете, вероятно, действительно только заботиться о № 1 в списке. Системное значение по умолчанию и консольное кодирование не настолько важны, если Вы не должны смотреть на вывод, и/или Вы используете встроенный unicode () функция (без любых параметров кодирования) вместо string.decode () функция. Я записал демонстрационную функцию, которую я вставлю в нижнюю часть этой гигантской путаницы, что я надеюсь, правильно демонстрирует объекты в моем списке. Вот часть вывода, когда я выполняю символ 'ó' через демонстрационную функцию, показывая, как различные методы реагируют на символ, как введено. Мое системное кодирование и консольный вывод оба установлены на utf_8 для этого выполнения:

'�' = original char  repr(char)='\xf3'
'?' = unicode(char) ERROR: 'utf8' codec can't decode byte 0xf3 in position 0: unexpected end of data
'ó' = char.decode('latin_1')  repr(char.decode('latin_1'))=u'\xf3'
'?' = char.decode('utf_8')  ERROR: 'utf8' codec can't decode byte 0xf3 in position 0: unexpected end of data

Теперь я изменю систему и консольное кодирование к latin_1, и я получаю этот вывод для того же входа:

'ó' = original char  repr(char)='\xf3'
'ó' = unicode(char)  repr(unicode(char))=u'\xf3'
'ó' = char.decode('latin_1')  repr(char.decode('latin_1'))=u'\xf3'
'?' = char.decode('utf_8')  ERROR: 'utf8' codec can't decode byte 0xf3 in position 0: unexpected end of data

Заметьте, что 'исходные' символьные дисплеи правильно и встроенный unicode () функционируют работы теперь.

Теперь я возвращаю свой консольный вывод к utf_8.

'�' = original char  repr(char)='\xf3'
'�' = unicode(char)  repr(unicode(char))=u'\xf3'
'�' = char.decode('latin_1')  repr(char.decode('latin_1'))=u'\xf3'
'?' = char.decode('utf_8')  ERROR: 'utf8' codec can't decode byte 0xf3 in position 0: unexpected end of data

Здесь все все еще работает то же прошлым разом, когда но консоль не может отобразить вывод правильно. И т.д. Функция ниже также отображает больше информации, которую это и надо надеяться помогло бы кому-то выяснить, где разрыв в их понимании. Я знаю, что вся эта информация находится в других местах и более тщательно имела дело с там, но я надеюсь, что это было бы хорошей точкой начала для кого-то пытающегося получить кодирование с Python и/или sqlite. Идеи являются прекрасными, но иногда исходный код может сохранить Вас день или две из попытки выяснить то, что функции делают что.

Отказ от ответственности: Я не эксперт по кодированию, я соединил это для помощи моему собственному пониманию. Я продолжал основываться на нем, когда я должен был, вероятно, начать передавать функции как аргументы для предотвращения такого большого количества избыточного кода, поэтому если я могу я делать это более кратким. Кроме того, utf_8 и latin_1 ни в коем случае не являются единственными схемами кодирования, они - просто два, которые я играл вокруг с тем, потому что я думаю, что они обрабатывают все, в чем я нуждаюсь. Добавьте свои собственные схемы кодирования к демонстрационной функции и протестируйте свой собственный вход.

Еще одна вещь: существуют по-видимому сумасшедшие разработчики приложений, делающие жизнь, трудную в Windows.

#!/usr/bin/env python
# -*- coding: utf_8 -*-

import os
import sys

def encodingDemo(str):
    validStrings = ()
    try:        
        print "str =",str,"{0} repr(str) = {1}".format(type(str), repr(str))
        validStrings += ((str,""),)
    except UnicodeEncodeError as ude:
        print "Couldn't print the str itself because the console is set to an encoding that doesn't understand some character in the string.  See error:\n\t",
        print ude
    try:
        x = unicode(str)
        print "unicode(str) = ",x
        validStrings+= ((x, " decoded into unicode by the default system encoding"),)
    except UnicodeDecodeError as ude:
        print "ERROR.  unicode(str) couldn't decode the string because the system encoding is set to an encoding that doesn't understand some character in the string."
        print "\tThe system encoding is set to {0}.  See error:\n\t".format(sys.getdefaultencoding()),  
        print ude
    except UnicodeEncodeError as uee:
        print "ERROR.  Couldn't print the unicode(str) because the console is set to an encoding that doesn't understand some character in the string.  See error:\n\t",
        print uee
    try:
        x = str.decode('latin_1')
        print "str.decode('latin_1') =",x
        validStrings+= ((x, " decoded with latin_1 into unicode"),)
        try:        
            print "str.decode('latin_1').encode('utf_8') =",str.decode('latin_1').encode('utf_8')
            validStrings+= ((x, " decoded with latin_1 into unicode and encoded into utf_8"),)
        except UnicodeDecodeError as ude:
            print "The string was decoded into unicode using the latin_1 encoding, but couldn't be encoded into utf_8.  See error:\n\t",
            print ude
    except UnicodeDecodeError as ude:
        print "Something didn't work, probably because the string wasn't latin_1 encoded.  See error:\n\t",
        print ude
    except UnicodeEncodeError as uee:
        print "ERROR.  Couldn't print the str.decode('latin_1') because the console is set to an encoding that doesn't understand some character in the string.  See error:\n\t",
        print uee
    try:
        x = str.decode('utf_8')
        print "str.decode('utf_8') =",x
        validStrings+= ((x, " decoded with utf_8 into unicode"),)
        try:        
            print "str.decode('utf_8').encode('latin_1') =",str.decode('utf_8').encode('latin_1')
        except UnicodeDecodeError as ude:
            print "str.decode('utf_8').encode('latin_1') didn't work.  The string was decoded into unicode using the utf_8 encoding, but couldn't be encoded into latin_1.  See error:\n\t",
            validStrings+= ((x, " decoded with utf_8 into unicode and encoded into latin_1"),)
            print ude
    except UnicodeDecodeError as ude:
        print "str.decode('utf_8') didn't work, probably because the string wasn't utf_8 encoded.  See error:\n\t",
        print ude
    except UnicodeEncodeError as uee:
        print "ERROR.  Couldn't print the str.decode('utf_8') because the console is set to an encoding that doesn't understand some character in the string.  See error:\n\t",uee

    print
    print "Printing information about each character in the original string."
    for char in str:
        try:
            print "\t'" + char + "' = original char {0} repr(char)={1}".format(type(char), repr(char))
        except UnicodeDecodeError as ude:
            print "\t'?' = original char  {0} repr(char)={1} ERROR PRINTING: {2}".format(type(char), repr(char), ude)
        except UnicodeEncodeError as uee:
            print "\t'?' = original char  {0} repr(char)={1} ERROR PRINTING: {2}".format(type(char), repr(char), uee)
            print uee    

        try:
            x = unicode(char)        
            print "\t'" + x + "' = unicode(char) {1} repr(unicode(char))={2}".format(x, type(x), repr(x))
        except UnicodeDecodeError as ude:
            print "\t'?' = unicode(char) ERROR: {0}".format(ude)
        except UnicodeEncodeError as uee:
            print "\t'?' = unicode(char)  {0} repr(char)={1} ERROR PRINTING: {2}".format(type(x), repr(x), uee)

        try:
            x = char.decode('latin_1')
            print "\t'" + x + "' = char.decode('latin_1') {1} repr(char.decode('latin_1'))={2}".format(x, type(x), repr(x))
        except UnicodeDecodeError as ude:
            print "\t'?' = char.decode('latin_1')  ERROR: {0}".format(ude)
        except UnicodeEncodeError as uee:
            print "\t'?' = char.decode('latin_1')  {0} repr(char)={1} ERROR PRINTING: {2}".format(type(x), repr(x), uee)

        try:
            x = char.decode('utf_8')
            print "\t'" + x + "' = char.decode('utf_8') {1} repr(char.decode('utf_8'))={2}".format(x, type(x), repr(x))
        except UnicodeDecodeError as ude:
            print "\t'?' = char.decode('utf_8')  ERROR: {0}".format(ude)
        except UnicodeEncodeError as uee:
            print "\t'?' = char.decode('utf_8')  {0} repr(char)={1} ERROR PRINTING: {2}".format(type(x), repr(x), uee)

        print

x = 'ó'
encodingDemo(x)

Много благодарит за ответы ниже и особенно к @John Machin для ответа так полностью.

65
задан shuttle87 28 July 2018 в 09:25
поделиться

4 ответа

Я до сих пор не знаю, есть ли способ правильно преобразовать 'ó' из latin-1 в utf-8 и не искажать его

repr () и unicodedata.name () - ваши друзья, когда дело доходит до для отладки таких проблем:

>>> oacute_latin1 = "\xF3"
>>> oacute_unicode = oacute_latin1.decode('latin1')
>>> oacute_utf8 = oacute_unicode.encode('utf8')
>>> print repr(oacute_latin1)
'\xf3'
>>> print repr(oacute_unicode)
u'\xf3'
>>> import unicodedata
>>> unicodedata.name(oacute_unicode)
'LATIN SMALL LETTER O WITH ACUTE'
>>> print repr(oacute_utf8)
'\xc3\xb3'
>>>

Если вы отправите oacute_utf8 на терминал, который настроен для latin1, вы получите A-тильду с последующим надстрочным индексом-3.

Я перешел на строки Unicode.

Что вы называете строками Unicode? UTF-16?

Что дает? После прочтения этого, описывающего ту же ситуацию, в которой я оказался, кажется, что совет состоит в том, чтобы игнорировать другие советы и в конце концов использовать 8-битные байтовые строки.

Не представляю, как вам это кажется. Рассказываемая история заключалась в том, что объекты Unicode в кодировке Python и UTF-8 в базе данных были лучшим вариантом. Однако Мартин ответил на исходный вопрос, указав метод («фабрику текстов»), позволяющий OP использовать latin1 - это НЕ являлось рекомендацией!

Обновите в ответ на эти дополнительные вопросы, поднятые в комментарии:

Я не понял, что символы Юникода все еще содержат неявную кодировку. Я правильно говорю?

Нет. Кодировка - это отображение между Unicode и чем-то еще, и наоборот. Символ Unicode не имеет кодировки, неявной или иной.

Мне кажется, что unicode ("\ xF3") и "\ xF3" .decode ('latin1') одинаковы при вычислении с помощью repr ().

Что? Мне это не кажется:

>>> unicode("\xF3")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xf3 in position 0: ordinal
not in range(128)
>>> "\xF3".decode('latin1')
u'\xf3'
>>>

Возможно, вы имели в виду: u '\ xf3' == '\ xF3'.decode (' latin1 ') ... это определенно правда.

Также верно, что unicode (str_object, encoding) делает то же самое, что и str_object.decode (encoding) ... включая взрывы, когда предоставляется несоответствующая кодировка.

Это счастливое обстоятельство.

То, что первые 256 символов в Unicode одинаковы, код для кода, поскольку 256 символов в latin1 - хорошая идея. Поскольку все 256 возможных символов latin1 отображаются в Unicode, это означает, что ЛЮБОЙ 8-битный байт, ЛЮБОЙ объект Python str может быть декодирован в Unicode без возникновения исключения. Это так и должно быть.

Однако есть люди, которые путают два совершенно разных понятия: «мой сценарий выполняется до завершения без возникновения каких-либо исключений» и «мой сценарий не содержит ошибок». Для них latin1 - это «ловушка и заблуждение».

Другими словами, если у вас есть файл, который на самом деле закодирован в cp1252, gbk или koi8-u или что-то еще, и вы декодируете его с помощью latin1, полученный Unicode будет полным мусором, а Python (или любой другой язык) не будет отмечать ошибка - у него нет возможности узнать, что вы совершили глупость.

или unicode ("str") всегда будет возвращать правильное декодирование?

Точно так же, с кодировкой по умолчанию ascii, он вернет правильный unicode, если файл действительно закодирован в ASCII. Иначе взорвется.

Точно так же, если вы укажете правильную кодировку или ту, которая является надмножеством правильной кодировки, вы получите правильный результат. В противном случае вы получите тарабарщину или исключение.

Вкратце: ответ отрицательный.

Если нет, когда я получаю строку python, в которой есть любой возможный набор символов, как мне узнать, как ее декодировать?

Если объект str является допустимым XML-документом, он будет указан заранее. По умолчанию используется UTF-8. Если это правильно построенная веб-страница, ее следует указать заранее (ищите "кодировку"). К сожалению, многие авторы веб-страниц лгут сквозь зубы (ISO-8859-1 aka latin1, должен быть Windows-1252 aka cp1252; не тратьте ресурсы на декодирование gb2312, используйте вместо этого gbk). Вы можете получить подсказки по национальности / языку веб-сайта.

Всегда стоит попробовать UTF-8. Если данные в формате ascii, все будет нормально, потому что ascii является подмножеством utf8. Строка текста, которая была написана с использованием символов, отличных от ascii, и была закодирована в кодировке, отличной от utf8, почти наверняка потерпит неудачу с исключением, если вы попытаетесь декодировать ее как utf8.

Все вышеперечисленные эвристики и многие другие статистические данные инкапсулированы в chardet , модуле для угадывания кодировки произвольных файлов. Обычно это хорошо работает. Однако вы не можете сделать программное обеспечение защищенным от идиотов. Например, если вы объедините файлы данных, записанные некоторые с кодировкой A, а некоторые с кодировкой B, и передадите результат в chardet, ответом, скорее всего, будет кодировка C с пониженным уровнем достоверности, например. 0.8. Всегда проверяйте доверительную часть ответа .

Если все остальное не помогает:

(1) Попробуйте спросить здесь, с небольшим образцом в начале ваших данных ... print repr (your_data [: 400]) ... и любая дополнительная информация о его происхождении, которая у вас есть.

(2) Недавние российские исследования методов восстановления забытых паролей , кажется, вполне применимы для получения неизвестных кодировок.

Обновление 2 Кстати, не пора ли задать еще один вопрос? -)

Еще кое-что:по-видимому, существуют символы, которые Windows использует в качестве Unicode для определенных символов, которые не являются правильным Unicode для этого символа, поэтому вам, возможно, придется сопоставить эти символы с правильными, если вы хотите использовать их в других программах, которые ожидают эти символы в правильное место.

Это делает не Windows; это группа сумасшедших разработчиков приложений. Возможно, вы не перефразировали, а процитировали начальный абзац статьи о effbot, о которой вы говорили:

Некоторые приложения добавляют символы CP1252 (Windows, Западная Европа) в документы, помеченные как ISO 8859-1 (Latin 1) или другие кодировки. Эти символы не являются действительными символами ISO-8859-1 и могут вызвать всевозможные проблемы в приложениях обработки и отображения.

Предпосылки:

Диапазон от U + 0000 до U + 001F включительно обозначен в Юникоде как «управляющие символы C0». Они также существуют в кодах ASCII и latin1 с теми же значениями. К ним относятся такие знакомые вещи, как возврат каретки, перевод строки, звонок, возврат, табуляция и другие, которые используются редко.

Диапазон от U + 0080 до U + 009F включительно обозначен в Юникоде как «управляющие символы C1». Они существуют также в latin1 и включают 32 символа, которые никто за пределами unicode.org не может представить себе никакого возможного использования.

Следовательно, если вы запустите подсчет частоты символов в данных Unicode или latin1 и обнаружите какие-либо символы в этом диапазоне, ваши данные будут повреждены. Универсального решения не существует; это зависит от того, как он был испорчен.Символы могут иметь то же значение, что и символы cp1252 в тех же позициях, и, таким образом, решение effbot будет работать. В другом случае, который я недавно рассматривал, изворотливые символы, по-видимому, были вызваны объединением текстовых файлов, закодированных в UTF-8, и другой кодировки, которую необходимо было вывести на основе частот букв на (человеческом) языке, на котором файлы были записано на.

34
ответ дан 24 November 2019 в 15:30
поделиться

Я исправил эту проблему pysqlite, установив:

conn.text_factory = lambda x: unicode(x, 'utf-8', 'ignore')

По умолчанию для text_factory установлено значение unicode (), которое будет использовать текущую кодировку по умолчанию (ascii на моей машине)

19
ответ дан 24 November 2019 в 15:30
поделиться

Конечно, есть. Но ваши данные уже разбиты в базе данных, поэтому вам придется их исправить:

>>> print u'Sigur Rós'.encode('latin-1').decode('utf-8')
Sigur Rós
8
ответ дан 24 November 2019 в 15:30
поделиться

UTF-8 - это кодировка по умолчанию для баз данных SQLite. Это проявляется в таких ситуациях, как «SELECT CAST (x'52C3B373 'AS TEXT);». Однако библиотека SQLite C на самом деле не проверяет, является ли строка, вставленная в БД, допустимой UTF-8.

Если вы вставите объект Unicode Python (или объект str в 3.x), библиотека Python sqlite3 автоматически преобразует его в UTF-8. Но если вы вставите объект str, он просто предположит , что это строка UTF-8, потому что Python 2.x "str" ​​не знает его кодировки. Это одна из причин предпочесть строки Unicode.

Однако, если ваши данные изначально повреждены, это не поможет.

Чтобы исправить данные, выполните

db.create_function('FIXENCODING', 1, lambda s: str(s).decode('latin-1'))
db.execute("UPDATE TheTable SET TextColumn=FIXENCODING(CAST(TextColumn AS BLOB))")

для каждого текстового столбца в базе данных.

21
ответ дан 24 November 2019 в 15:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: