SQLite的,蟒蛇,Unicode和非UTF数据SQLite的,蟒蛇,Unicode和非UTF数据

2019-05-13 04:00发布

我开始使用蟒蛇试图在sqlite的存储字符串,并得到了消息:

sqlite3.ProgrammingError:除非你使用的是可以解释的8位字节串(如text_factory = STR)一text_factory不得使用8位字节串。 强烈建议您改为只需切换您的应用程序Unicode字符串。

好吧,我切换到Unicode字符串。 然后,我开始收到消息:

sqlite3.OperationalError:无法解码为UTF-8列“tag_artist”文本“西格尔罗斯”

当试图检索数据库的数据。 更多的研究,我开始编码它在UTF8,但随后“西格尔罗斯”开始看起来像“西格尔Rós”

注:我的控制台被设置在“LATIN_1”显示为@约翰梅钦指出。

是什么赋予了? 看完这个 ,恰恰说明了同样的情况,我在,它看来,如果建议是忽略其他建议毕竟使用8位字节串。

我不知道很多关于Unicode和UTF之前,我开始这个过程。 我已经学到了很多在过去的几个小时,但我仍然懵懂是否有正确转换的方式“O”来自拉丁美洲-1为UTF-8,而不是裂伤它。 如果没有,为什么会SQLite的“强烈推荐”我在我的应用程序切换到unicode字符串?


我要更新与总结,一切我已经在过去24小时内学会让别人在我的鞋能有一个简单的(ER)引导的一些示例代码这个问题。 如果我发布的信息以任何方式错误或误导性的请告诉我,我会更新,或者你资深的球员之一可以更新。


答案汇总

首先,我要声明的目标,因为我了解它。 在处理各种编码的目标,如果你正尝试将它们之间的转换,是要了解你的源编码是什么,然后将其转换使用源编码为Unicode,然后将其转换为所需的编码。 Unicode是一种碱和编码是基的子集的映射。 UTF_8有余地unicode的每个字符,而是因为他们不在同一个地方,例如,LATIN_1,在UTF_8编码并发送至LATIN_1控制台不会看你期望的方式的字符串。 在蟒蛇越来越为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。 这是可能的,虽然,你可以在Windows上的文件名混合在一起LATIN_1和UTF_8和任何其他字符。 有时,这些字符可以显示为框,其他时间他们只是看缺胳膊少腿,其他时候,他们看起来是正确的(重音字符和诸如此类的东西)。 继续。

(2)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”)功能,无需任何其他的编码参数被使用的一个。 如果说另一种方式,蟒蛇试图基于默认的系统编码“STR”解码为Unicode。

(3)如果你使用IDLE或命令行Python,我认为你的控制台将根据默认的系统编码显示。 我使用的PyDev使用Eclipse出于某种原因,所以我不得不去到我的项目设置,编辑我的测试脚本的启动配置属性,转到Common选项卡,并从Latin-1的改变控制台为UTF-8,这样我可以直观地确认我在做什么是工作。

(4)如果你想有一些测试字符串,如

test_str = "ó"

在你的源代码,那么你将要告诉您使用的是该文件是什么样的编码的蟒蛇。 (供参考:当我键入了错误的编码,我不得不按Ctrl-Z,因为我的文件变得不可读。)这是很容易把一条线,像这样在你的源代码文件的顶部来完成:

# -*- coding: utf_8 -*-

如果你没有这个信息,蟒蛇尝试解析您的代码作为默认的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()函数,系统默认和控制台的编码并不是那么重要。 我写了一个演示功能,我会贴到这个巨大的混乱,我希望正确表明了我的列表中的项目的底部。 下面是一些输出的时候我通过演示功能运行字符“O”,表示各种方法如何反应,从而将字符作为输入。 我的系统编码和控制台输出都被设置为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)

许多感谢您的答案下方,尤其是@约翰梅钦回答得如此彻底。

Answer 1:

我还是懵懂的是否有正确转换的方式“O”来自拉丁美洲-1为UTF-8,而不是裂伤它

再版()和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。

我切换到Unicode字符串。

什么是你调用Unicode字符串? UTF-16?

是什么赋予了? 读这篇文章,正是描述了同样的情况,我在后,它看起来好像建议是忽略其他建议毕竟使用8位字节串。

我无法想象它是如何似乎很给你。 当时正在传达的说法是,在Python Unicode对象和UTF-8编码数据库是要走的路。 然而马丁回答原来的问题,给人的OP,以便能够使用latin1的方法(“文字厂”) - 这并不构成推荐!

更新以响应评论提出这些进一步的问题:

我不明白的是,Unicode字符仍然包含一个隐含的编码。 我是不是说对不对?

号的编码是Unicode和其他的东西,反之亦然之间的映射。 Unicode字符没有编码,隐含的或其他方式。

它看起来像我的unicode( “\ XF3”)和 “\ XF3” .decode( 'LATIN1')是相同的,当与再版()进行评估。

说什么? 它看起来并不像它给我:

>>> 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) ......包括当提供不适当的编码吹起来。

那是一个快乐的环境

在Unicode的前256个字符的代码相同,代码,如LATIN1的256个字符是一个好主意。 因为所有的256个可能的latin1字符被映射到Unicode,这意味着任意的8位字节,ANY的Python STR对象可被解码成unicode而不引起的异常。 这是理所应当的。

但是存在谁混淆了两个完全不同的概念某些人士:与“我的剧本是没有错误的”,“没有任何异常被提出我的脚本运行完成”。 对他们来说,latin1的是“圈套和妄想”。

换句话说,如果你有这实际上是在CP1252或GBK或KOI8-U或任何编码的文件,并且对其进行解码使用latin1的,所产生的统一将是彻底的垃圾和Python(或任何其他语言)不会标志一个错误 - - 它没有知道你有一个COMMITED愚蠢的方式。

或者是unicode(“STR”)将始终返回正确的解码?

就这样,默认编码为ASCII,它会返回正确的Unicode文件是否以ASCII编码的实际。 否则,它会炸毁。

同样,如果您指定正确的编码,或一个是正确的编码的超集,你会得到正确的结果。 否则,你会得到乱码或异常。

总之:答案是否定的。

如果不是这样,当我收到有任何可能的字符在它设置,我怎么知道一个python STR如何判读?

如果STR对象是有效的XML文档,它会被指定了前面。 默认为UTF-8。 如果这是一个正确构造的网页时,应指定前面(寻找“字符集”)。 不幸的是网页的许多作家骗通过他们的牙齿(ISO-8859-1又名latin1的,应该是Windows 1252又名CP1252;不浪费资源,试图解码GB2312,GBK使用代替)。 你可以从网站的国籍/语言线索。

UTF-8是永远值得尝试。 如果数据是ASCII,它会正常工作,因为ASCII是UTF-8的一个子集。 已使用非ASCII字符写,如果你尝试将其作为UTF8解码已经比其他UTF8编码几乎肯定会失败,一个异常编码文本字符串。

以上所有的启发,更和大量的统计数据被封装在chardet的 ,一个模块猜测的任意文件的编码。 它通常效果很好。 但是你不能让软件傻瓜型。 例如,如果您连接写入一些用编码A和一些用编码B数据文件,并且将结果提供给chardet模块,答案很可能是与例如0.8置信水平降低编码℃。 经常检查答案的信心部分

如果一切都失败:

(1)试着问这里,从数据的前一个小样本... print repr(your_data[:400]) ......和任何有关它的出处,你有抵押物的信息。

(2)最近的俄罗斯研究用于恢复遗忘的密码技术似乎非常适用于推导未知的编码。

BTW 更新2,是不是你打开了另一个问题时- ?)

还有一两件事:有很明显的是Windows使用为Unicode的某些字符不属于该字符的正确的Unicode字符,所以您可能需要这些字符正确映射的人,如果你想在其他程序使用它们在合适的地方期待这些字符。

这不是窗口都这样做; 这是一群疯狂的应用程序开发人员。 你可能有更多的不理解,但转述引用你提到的effbot文章的开头段:

有些应用程序添加CP1252(Windows中,西欧)字符标为ISO 8859-1(拉丁文1)或其他编码文件。 这些字符不是有效的ISO-8859-1,并可能引发各种在处理和显示应用的问题。

背景:

到U + 001F以下的范围U + 0000以Unicode表示为“C0控制字符”。 这些还存在于ASCII和Latin1的具有相同涵义。 它们包括familar事情回车,换行,钟,退格键,选项卡,和其他人很少使用。

到U + 009F以下的范围U + 0080以Unicode表示为“C1控制字符”。 这些还存在于latin1的,并包括32个字符unicode.org之外没有人能想象任何可能的用途。

因此,如果你运行一个字符频率指望你的Unicode或latin1的数据,你会发现在该范围内的任何字符,你的数据被破坏。 有没有通用的解决方案; 这取决于它损坏的原因。 该字符可能具有的含义为CP1252字符一样在相同的位置,从而effbot的解决方案会奏效。 在我一直在寻找最近另一起案件中,狡猾的人物似乎已经在UTF-8编码的串联文本文件和其它的编码,这需要基于(人)的字母频率来推断语言的文件引起了写入了数据。



Answer 2:

UTF-8是SQLite数据库的默认编码。 这显示了在像情况 “SELECT CAST(x'52C3B373' AS TEXT);”。 然而,SQLite的C库实际上不检查插入到数据库字符串是否是有效的UTF-8。

如果你插入一个Python的Unicode对象(或3.X STR对象),Python的sqlite3的库会自动将其转换为UTF-8。 但是,如果你插入一个海峡的对象,它只是假设字符串是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))")

在你的数据库中每个文本列。



Answer 3:

我通过设置固定的这个问题pysqlite:

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

默认情况下,text_factory被设置为Unicode(),这将使用当前的默认编码(ASCII我的机器上)



Answer 4:

当然有。 但是,你的数据已经打破了数据库,所以你需要修复它:

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


Answer 5:

我的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位字节串,除非...

编辑

sys.setdefaultencoding是一个肮脏的黑客 。 是的,它可以解决UTF-8的问题,但一切是有代价的。 欲了解更多详情,请参阅以下链接:

  • 为什么sys.setdefaultencoding函数()将打破代码
  • 为什么我们需要sys.setdefaultencoding函数(“UTF-8”)在吡啶脚本?


文章来源: SQLite, python, unicode, and non-utf data