Я начал с попытки сохранить строки в sqlite с помощью python и получил сообщение:
sqlite3.ProgrammingError: вы должны не используйте 8-битные байты, если вы используйте text_factory, который может интерпретировать 8-битные байты (например text_factory = ул). Настоятельно рекомендуется, чтобы вы вместо этого просто приложение к строкам Unicode.
Хорошо, я переключился на строки Unicode. Затем я начал получать сообщение:
sqlite3.OperationalError: не удалось декодировать в столбец UTF-8 'tag_artist' с текстом 'Sigur Rós'
при попытке извлечь данные из db. Больше исследований, и я начал кодировать его в utf8, но затем "Sigur Rós" начинает выглядеть как "Sigur Rós"
примечание: Моя консоль была установлена для отображения в 'latin_1', как указал @John Machin.
Что дает? После прочтения этого, описывающего точно такую же ситуацию, в которой я нахожусь, кажется, что совет должен игнорировать другой совет и использовать 8-битный bytestrings в конце концов.
Я не знал много о unicode и utf, прежде чем начал этот процесс. За последние пару часов я узнал немало, но я все еще не знаю, есть ли способ правильно преобразовать '-' из латинского-1 в utf-8 и не калечить его. Если этого не произойдет, почему SQLite настоятельно рекомендует переключить мое приложение на строки unicode?
Я собираюсь обновить этот вопрос с помощью сводного и некоторого примерного кода всего, что я узнал за последние 24 часа, чтобы у кого-то в моей обуви мог быть легкий (э-ридер). Если информация, которую я публикую, неверна или вводит в заблуждение каким-либо образом, скажите мне, и я обновлю, или один из вас может обновить старших ребят.
Резюме ответов
Позвольте мне сначала изложить цель, насколько я ее понимаю. Целью обработки различных кодировок, если вы пытаетесь преобразовать их между собой, является понимание того, что такое исходная кодировка, а затем преобразовать ее в unicode с использованием этой исходной кодировки, а затем преобразовать ее в нужную кодировку. Unicode является базой, а кодировки - отображением подмножеств этой базы. utf_8 имеет место для каждого символа в юникоде, но поскольку они не находятся на том же месте, что, например, latin_1, строка, закодированная в utf_8 и отправленная в консоль latin_1, не будет выглядеть так, как вы ожидаете. В python процесс перехода на unicode и в другую кодировку выглядит следующим образом:
str.decode('source_encoding').encode('desired_encoding')
или если str уже находится в Юникоде
str.encode('desired_encoding')
Для sqlite я на самом деле не хотел его снова закодировать, я хотел его декодировать и оставить в формате Unicode. Вот четыре вещи, которые вам, возможно, нужно знать, когда вы пытаетесь работать с юникодом и кодировками в python.
- Кодирование строки, с которой вы хотите работать, и кодировку, которую вы хотите получить.
- Системное кодирование.
- Консольная кодировка.
- Кодирование исходного файла
Разработка:
(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 или пиктон командной строки, я думаю, что ваша консоль отобразится в соответствии с системной кодировкой по умолчанию. По какой-то причине я использую pydev с eclipse, поэтому мне пришлось зайти в настройки моего проекта, отредактировать параметры конфигурации запуска моего теста script, перейти на вкладку "Общие" и сменить консоль с латинского-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() (без каких-либо параметров кодирования) вместо функции string.decode(). Я написал демо-функцию, которую я вставляю в дно этого гигантского беспорядка, который, я надеюсь, правильно демонстрирует элементы в моем списке. Вот некоторые из результатов, когда я запускаю символ "-" через демонстрационную функцию, показывая, как различные методы реагируют на символ как на вход. Моя системная кодировка и вывод на консоль установлены для 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:\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 за то, что вы так хорошо ответили.