Данные SQLite, python, unicode и не utf


я начал с попытки сохранить строки в sqlite с помощью python, и получил сообщение:

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

Ок, я переключился на строки Юникода. Затем я начал получать сообщение:

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

при попытке получить данные из БД. Больше исследований, и я начал кодировать его в utf8, но затем "Sigur Rós" начинает выглядеть как "Sigur RÃ3s"

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

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

Я не знал много о 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')

или если str уже находится в 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 ("str") без каких-либо других параметры кодирования. Чтобы сказать, что по-другому, python пытается декодировать "str" в unicode на основе системной кодировки по умолчанию.

(3) Если вы используете IDLE или python командной строки, Я думаю, что ваша консоль будет отображаться в соответствии с системной кодировкой по умолчанию. По какой-то причине я использую pydev с eclipse, поэтому мне пришлось войти в настройки моего проекта, отредактировать свойства конфигурации запуска моего тестового сценария, перейти на вкладку Common и изменить консоль с latin-1 на utf-8, поэтому то, что я мог визуально подтвердить, что я делал, работало.

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

test_str = "ó"

в исходном коде, то вам придется указать Python, какую кодировку вы используете в этом файле. (FYI: когда я ошибся в кодировке, мне пришлось 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 () (без каких-либо параметров кодирования) вместо строка.функция декодирования (). Я написал демонстрационную функцию, которую я вставлю в нижнюю часть этого гигантского беспорядка, который, я надеюсь, правильно демонстрирует элементы в моем списке. Вот некоторые из выходных данных, когда я запускаю символ ' ó ' через демонстрационную функцию, показывая, как различные методы реагируют на символ в качестве входных данных. Моя системная кодировка и консольный вывод имеют значение utf_8 для этого запуска:

'�' = original char <type 'str'> repr(char)='xf3'
'?' = unicode(char) ERROR: 'utf8' codec can't decode byte 0xf3 in position 0: unexpected end of data
'ó' = char.decode('latin_1') <type 'unicode'> 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 <type 'str'> repr(char)='xf3'
'ó' = unicode(char) <type 'unicode'> repr(unicode(char))=u'xf3'
'ó' = char.decode('latin_1') <type 'unicode'> 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 <type 'str'> repr(char)='xf3'
'�' = unicode(char) <type 'unicode'> repr(unicode(char))=u'xf3'
'�' = char.decode('latin_1') <type 'unicode'> 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:nt",
        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:nt".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:nt",
        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:nt",
            print ude
    except UnicodeDecodeError as ude:
        print "Something didn't work, probably because the string wasn't latin_1 encoded.  See error:nt",
        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:nt",
        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:nt",
            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:nt",
        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:nt",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 за столь подробный ответ.

5 64

5 ответов:

Я все еще не знаю, есть ли способ правильно конвертировать '-' из латинского-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, вы получите-Тильду, за которой следует Надстрочный индекс-3.

я перешел на Юникод.

что ты называешь Строки Юникода? UTF-16?

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

Я не могу себе представить,как это вам кажется. История, которая передавалась, заключалась в том, что объекты unicode в Python и кодировке UTF-8 в базе данных были способом пойти. Однако Мартин ответил на первоначальный вопрос, дав метод ("текстовая фабрика") для ОП, чтобы иметь возможность использовать latin1 - это не является рекомендацией!

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

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

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

это похоже на unicode ("\xF3") и"\xF3".декодирование ('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 без исключения. Так и должно быть.

однако существуют определенные люди, которые путают два совершенно разных понятия: "мой скрипт работает до завершения без каких-либо возникают исключения "и"мой скрипт безошибочен". Для них латин1 - это "ловушка и заблуждение".

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

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

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

короче: ответ-нет.

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

если объект str является допустимым XML-документом, он будет указан заранее. По умолчанию используется UTF-8. Если это правильно построенная веб-страница, она должна быть указана заранее (ищите "charset"). К сожалению, многие авторы веб-страниц лгут сквозь зубы (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 наверняка символы, которые не являются правильным Юникодом для этого символа, поэтому вам может потребоваться сопоставить эти символы с правильными, если вы хотите использовать их в других программах, которые ожидают эти символы в нужном месте.

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

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

Справочная информация:

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

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

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

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

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

тем не менее, это не поможет вам, если ваши данные сломаны для начала.

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

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

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

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

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

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

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

>>> print u'Sigur Rós'.encode('latin-1').decode('utf-8')
Sigur Rós

мои проблемы unicode с Python 2.x (Python 2.7.6, чтобы быть точным) исправлено это:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import unicode_literals
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

Он также решил ошибку, которую вы упоминаете прямо в начале сообщения:

sqlite3.ProgrammingError: вы не должны использовать 8-битные bytestrings, если только ...

EDIT

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