我首先尝试使用 python 在 sqlite 中存储字符串,并得到消息:
sqlite3.ProgrammingError:除非您使用可以解释 8 位字节串的 text_factory(如 text_factory = str),否则不得使用 8 位字节串。强烈建议您将应用程序切换为 Unicode 字符串。
好的,我切换到 Unicode 字符串。然后我开始收到消息:
sqlite3.OperationalError:无法解码为带有文本“Sigur Rós”的 UTF-8 列“tag_artist”
尝试从数据库中检索数据时。更多研究,我开始用 utf8 对其进行编码,但随后“Sigur Rós”开始看起来像“Sigur Rós”
注意: 正如@John Machin 指出的那样,我的控制台设置为显示在“latin_1”中。
是什么赋予了?在阅读完这篇文章后,描述了与我完全相同的情况,似乎建议是忽略其他建议并毕竟使用 8 位字节串。
在我开始这个过程之前,我对 unicode 和 utf 了解不多。在过去的几个小时里我学到了很多东西,但我仍然不知道是否有一种方法可以正确地将 'ó' 从 latin-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 中一样。但是,您可以将 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,我认为你的控制台会按照默认的系统编码显示。由于某种原因,我在 Eclipse 中使用 pydev,所以我必须进入我的项目设置,编辑我的测试脚本的启动配置属性,转到 Common 选项卡,然后将控制台从 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 的彻底回答。