我首先尝试使用python将字符串存储在sqlite中,并得到以下消息:
sqlite3.ProgrammingError:除非使用可以解释8位字节串的text_factory(如text_factory = str),否则不得使用8位字节串。强烈建议您改为将应用程序切换为Unicode字符串。
好的,我切换到Unicode字符串。然后我开始收到消息:
sqlite3.OperationalError:无法解码为文本为“ SigurRós”的UTF-8列“ tag_artist”
尝试从数据库检索数据时。进行了更多研究,我开始在utf8中对其进行编码,但随后“ SigurRós”开始看起来像“ SigurRós”
注意: 正如@John Machin指出的那样,我的控制台设置为在“ latin_1”中显示。
是什么赋予了?看完这篇文章并描述了我所处的完全相同的情况之后,似乎该建议是忽略其他建议并毕竟使用8位字节串。
在开始此过程之前,我对unicode和utf不太了解。在过去的几个小时中,我学到了很多东西,但是我仍然不知道是否有一种方法可以将“ó”从拉丁文1正确地转换为utf-8,而不是对其进行处理。如果没有,为什么sqlite强烈建议我将应用程序切换为unicode字符串?
我将使用最近24小时内我学到的所有内容的摘要和一些示例代码来更新此问题,以便穿鞋的人可以轻松获得指南。如果我发布的信息有误或以任何方式引起误导,请告诉我,我会更新,或者你们中的一位资深人士可以更新。
答案摘要
让我先说一说我了解的目标。如果要在各种编码之间进行转换,则处理各种编码的目的是要了解源编码是什么,然后使用该源编码将其转换为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格式。在尝试使用python中的unicode和编码时,可能需要注意以下四点。
- 您要使用的字符串的编码以及要获取的字符串的编码。
- 系统编码。
- 控制台编码。
- 源文件的编码
详细说明:
(1)从源读取字符串时,它必须具有某种编码,例如latin_1或utf_8。就我而言,我从文件名中获取字符串,所以不幸的是,我可能会获得任何类型的编码。Windows XP使用UCS-2(Unicode系统)作为其本机字符串类型,这对我来说似乎是作弊行为。对我来说幸运的是,大多数文件名中的字符都不会由多个源编码类型组成,而且我认为我的全部要么完全是latin_1,完全是utf_8,要么仅仅是纯ascii(这是两个字符的子集)那些)。因此,我只是阅读它们并对其进行解码,就好像它们仍在latin_1或utf_8中一样。不过,在Windows上的文件名中,可能有latin_1和utf_8以及其他任何字符混合在一起的可能。有时这些字符会显示为方框,有时候,它们看起来像是被弄乱了,而有时候,它们看起来是正确的(带重音的字符等等)。继续。
(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一起使用,因此必须进入项目设置,编辑测试脚本的启动配置属性,转到“通用”选项卡,并将控制台从latin-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 <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如此详尽地回答。