Python sqlite3模块的text_factory属性的使用方法研究

写这篇文章,起源于要写一个脚本批量把CSV文件(文件采用GBK或utf-8编码)写入到sqlite数据库里。

Python版本:2.7.9

sqlite3模块提供了con = sqlite3.connect("D:\\text_factory.db3") 这样的方法来创建数据库(当文件不存在时,新建库),数据库默认编码为UTF-8,支持使用特殊sql语句设置编码

PRAGMA encoding = "UTF-8"; 
PRAGMA encoding = "UTF-16"; 
PRAGMA encoding = "UTF-16le"; 
PRAGMA encoding = "UTF-16be";    

但设置编码必须在main库之前,否则无法更改。 https://www.sqlite.org/pragma.html#pragma_encoding

认识text_factory属性,大家应该都是通过以下错误知晓的:

sqlite3.ProgrammingError: You must not use 8-bit bytestrings unless you use a text_factory that can interpret 8-bit bytestrings (like text_factory = str). It is highly recommended that you instead just switch your application to Unicode strings.

大意是推荐你把字符串入库之前转成unicode string,你要用bytestring字节型字符串(如ascii ,gbk,utf-8),需要加一条语句text_factory = str。

Python拥有两种字符串类型。标准字符串是单字节字符序列,允许包含二进制数据和嵌入的null字符。 Unicode 字符串是双字节字符序列,一个字符使用两个字节来保存,因此可以有最多65536种不同的unicode字符。尽管最新的Unicode标准支持最多100万个不同的字符,Python现在尚未支持这个最新的标准。

默认text_factory = unicode,原以为这unicode、str是函数指针,但貌似不是,是<type ‘unicode‘>和<type ‘str‘>

下面写了一段测试验证代码:

 1 # -*- coding: utf-8 -*-
 2 import sqlite3
 3 ‘‘‘
 4 GBK   UNIC  UTF-8
 5 B8A3  798F  E7 A6 8F  福
 6 D6DD  5DDE  E5 B7 9E  州
 7 ‘‘‘
 8
 9 con = sqlite3.connect(":memory:")
10 # con = sqlite3.connect("D:\\text_factory1.db3")
11 # con.executescript(‘PRAGMA encoding = "UTF-16";‘)
12 cur = con.cursor()
13
14 a_text      = "Fu Zhou"
15 gb_text     = "\xB8\xA3\xD6\xDD"
16 utf8_text   = "\xE7\xA6\x8F\xE5\xB7\x9E"
17 unicode_text= u"\u798F\u5DDE"
18
19 print ‘Part 1: con.text_factory=str‘
20 con.text_factory = str
21 print type(con.text_factory)
22 cur.execute("CREATE TABLE table1 (city);")
23 cur.execute("INSERT INTO table1 (city) VALUES (?);",(a_text,))
24 cur.execute("INSERT INTO table1 (city) VALUES (?);",(gb_text,))
25 cur.execute("INSERT INTO table1 (city) VALUES (?);",(utf8_text,))
26 cur.execute("INSERT INTO table1 (city) VALUES (?);",(unicode_text,))
27 cur.execute("select city from table1")
28 res = cur.fetchall()
29 print "--  result: %s"%(res)
30
31 print ‘Part 2: con.text_factory=unicode‘
32 con.text_factory = unicode
33 print type(con.text_factory)
34 cur.execute("CREATE TABLE table2 (city);")
35 cur.execute("INSERT INTO table2 (city) VALUES (?);",(a_text,))
36 # cur.execute("INSERT INTO table2 (city) VALUES (?);",(gb_text,))
37 # cur.execute("INSERT INTO table2 (city) VALUES (?);",(utf8_text,))
38 cur.execute("INSERT INTO table2 (city) VALUES (?);",(unicode_text,))
39 cur.execute("select city from table2")
40 res = cur.fetchall()
41 print "--  result: %s"%(res)
42
43 print ‘Part 3: OptimizedUnicode‘
44 con.text_factory = str
45 cur.execute("CREATE TABLE table3 (city);")
46 cur.execute("INSERT INTO table3 (city) VALUES (?);",(a_text,))
47 #cur.execute("INSERT INTO table3 (city) VALUES (?);",(gb_text,))
48 cur.execute("INSERT INTO table3 (city) VALUES (?);",(utf8_text,))
49 cur.execute("INSERT INTO table3 (city) VALUES (?);",(unicode_text,))
50 con.text_factory = sqlite3.OptimizedUnicode
51 print type(con.text_factory)
52 cur.execute("select city from table3")
53 res = cur.fetchall()
54 print "--  result: %s"%(res)
55
56 print ‘Part 4: custom fuction‘
57 con.text_factory = lambda x: unicode(x, "gbk", "ignore")
58 print type(con.text_factory)
59 cur.execute("CREATE TABLE table4 (city);")
60 cur.execute("INSERT INTO table4 (city) VALUES (?);",(a_text,))
61 cur.execute("INSERT INTO table4 (city) VALUES (?);",(gb_text,))
62 cur.execute("INSERT INTO table4 (city) VALUES (?);",(utf8_text,))
63 cur.execute("INSERT INTO table4 (city) VALUES (?);",(unicode_text,))
64 cur.execute("select city from table4")
65 res = cur.fetchall()
66 print "--  result: %s"%(res)

打印结果:

Part 1: con.text_factory=str
<type ‘type‘>
--  result: [(‘Fu Zhou‘,), (‘\xb8\xa3\xd6\xdd‘,), (‘\xe7\xa6\x8f\xe5\xb7\x9e‘,), (‘\xe7\xa6\x8f\xe5\xb7\x9e‘,)]
Part 2: con.text_factory=unicode
<type ‘type‘>
--  result: [(u‘Fu Zhou‘,), (u‘\u798f\u5dde‘,)]
Part 3: OptimizedUnicode
<type ‘type‘>
--  result: [(‘Fu Zhou‘,), (u‘\u798f\u5dde‘,), (u‘\u798f\u5dde‘,)]
Part 4: custom fuction
<type ‘function‘>
--  result: [(u‘Fu Zhou‘,), (u‘\u798f\u5dde‘,), (u‘\u7ec2\u5fd3\u7a9e‘,), (u‘\u7ec2\u5fd3\u7a9e‘,)]

Part 1:unicode被转换成了utf-8,utf-8和GBK被透传,写入数据库,GBK字符串被取出显示时,需要用类似‘gbk chars‘.decode("cp936").encode("utf_8")的语句进行解析print

Part 2:默认设置,注释的掉都会产生以上的经典错误,输入范围被限定在unicode对象或纯ascii码  

Part 3:自动优化,ascii为str对象,非ascii转为unicode对象

Part 4:GBK被正确转换,utf-8和unicode在存入数据库时,都被转为了默认编码utf-8存储,既‘\xe7\xa6\x8f\xe5\xb7\x9e‘,

In[16]: unicode(‘\xe7\xa6\x8f\xe5\xb7\x9e‘,‘gbk‘)
Out[16]: u‘\u7ec2\u5fd3\u7a9e‘

就得到了以上结果。

接着,用软件查看数据库里是如何存放的。

分别用官方的sqlite3.exe和SqliteSpy查看,sqlite3.exe因为用命令行界面,命令行用的是GBK显示;SqliteSpy则是用UTF显示,所以GBK显示乱码。这就再次印证了GBK被允许存放入数据库的时候,存放的是raw数据,并不会强制转为数据库的默认编码utf-8保存

Connection.text_factory使用此属性来控制我们可以从TEXT类型得到什么对象(我:这也印证写入数据库的时候,需要自己编码,不能依靠这个)。默认情况下,这个属性被设置为Unicode,sqlite3模块将会为TEXT返回Unicode对象。若你想返回bytestring对象,可以将它设置为str。

因为效率的原因,还有一个只针对非ASCII数据,返回Unicode对象,其它数据则全部返回bytestring对象的方法。要激活它,将此属性设置为sqlite3.OptimizedUnicode。

你也可以将它设置为任意的其它callabel,接收一个bytestirng类型的参数,并返回结果对象。《摘自http://www.360doc.com/content/11/1102/10/4910_161017252.shtml》

以上一段话是官方文档的中文版关于text_factory描述的节选。

综上,我谈谈我的看法*和使用建议:

1)sqlite3模块执行insert时,写入的是raw数据,写入前会根据text_factory属性进行类型判断,默认判断写入的是否为unicode对象;

2)使用fetchall()从数据库读出时,会根据text_factory属性进行转化。

3)输入字符串是GBK编码的bytestring,decode转为unicode写入;或加text_factory=str直接写入,读出时仍为GBK,前提需要数据库编码为utf-8,注意用sqlitespy查看是乱码。

4)输入字符串是Utf-8编码的bytestring,可以设置text_factory=str直接写入直接读出,sqlitespy查看正常显示。

5)如果不是什么高性能场景,入库前转成unicode,性能开销也很小,测试数据找不到了,像我这样话一整天研究这一行代码,不如让机器每次多跑零点几秒。。

*(因为没有查看sqlite3模块的源代码,所以只是猜测)

另外,附上数据库设置为UTF-16编码时,产生的结果,更乱,不推荐。

Part 1: con.text_factory=str
<type ‘type‘>
--  result: [(‘Fu Zhou‘,), (‘\xc2\xb8\xc2\xa3\xef\xbf\xbd\xef\xbf\xbd‘,), (‘\xe7\xa6\x8f\xe5\xb7\x9e‘,), (‘\xe7\xa6\x8f\xe5\xb7\x9e‘,)]
Part 2: con.text_factory=unicode
<type ‘type‘>
--  result: [(u‘Fu Zhou‘,), (u‘\u798f\u5dde‘,)]
Part 3: OptimizedUnicode
<type ‘type‘>
--  result: [(‘Fu Zhou‘,), (u‘\u798f\u5dde‘,), (u‘\u798f\u5dde‘,)]
Part 4: custom fuction
<type ‘function‘>
--  result: [(u‘Fu Zhou‘,), (u‘\u8d42\u62e2\u951f\u65a4\u62f7‘,), (u‘\u7ec2\u5fd3\u7a9e‘,), (u‘\u7ec2\u5fd3\u7a9e‘,)]

  

 
时间: 2024-10-29 03:31:37

Python sqlite3模块的text_factory属性的使用方法研究的相关文章

day02 Python 的模块,运算,数据类型以及方法

初识pyhton的模块: 什么是模块: 我的理解就是实现一个功能的函数,把它封装起来,在你需要使用的时候直接调用即可,我的印象里类似于shell 的单独函数脚本. python 的模块分为标准的和第三方的,标准的直接使用即可,第三方需要安装,可以使用pip 来安装模块,这个我们以后再讲. 模块都在哪里呢? 其实模块也是一个文件,我们通过搜索发现自带的模块都在python安装目录的base/lib下,第三方的模块则是在base/lib/site-packages 如何使用模块: 我们在使用模块的某

python sqlite3 数据库操作

SQLite3是python的内置模块,是一款非常小巧的嵌入式开源数据库软件. 1. 导入Python SQLite数据库模块 import sqlite3 2. python sqlite3模块的API """ sqlite3.connect(database [,timeout ,other optional arguments]) 该 API 打开一个到 SQLite 数据库文件 database 的链接.您可以使用 ":memory:" 来在 RA

python内置的sqlite3模块,使用其内置数据库

1.python内置的sqlite3模块,创建数据库中的表,并向表中插入数据,从表中取出所有行,以及输出行的数量. #!/usr/bin/env python3 #创建SQLite3内存数据库,并创建带有四个属性的sales表 #sqlite3模块,提供了一个轻量级的基于磁盘的数据库,不需要独立的服务器进程 import sqlite3 #使用‘:memory:’在内存中创建了一个数据库,创建了连接对象con来代表数据库 con = sqlite3.connect(':memory:') #创建

python安装sqlite3模块

Python安装sqlite3 环境为Ubuntu16.04 Apache2.4 Python2.7.13 django 1.8 今天部署apache+django,经过各种折腾,好不容易配置完了,发现错误Apache的日志里有一项 ImproperlyConfigured: Error loading either pysqlite2 or sqlite3 modules (tried in that order): No module named _sqlite3 于是打开Python 测试

[Python]sqlite3二进制文件存储问题(BLOB)(You must not use 8-bit bytestrings unless you use a text_factory...)

事情是这样的: 博主尝试用Python的sqlite3数据库存放加密后的用户名密码信息,表是这样的 CREATE TABLE IF NOT EXISTS user ( userID INTEGER PRIMARY KEY AUTOINCREMENT, userStudentID BLOB NOT NULL UNIQUE ON CONFLICT IGNORE, userPassword BLOB NOT NULL ); 其中userStudentID and UserPassword 储存成了BL

Python 3.6.0的sqlite3模块无法执行VACUUM语句

Python 3.6.0的sqlite3模块存在一个bug(见issue 29003),无法执行VACUUM语句. 一执行就出现异常: Traceback (most recent call last):  File "D:\desktop\cannot_vacuum.py", line 25, in <module>    conn.execute('VACUUM')sqlite3.OperationalError: cannot VACUUM from within a

临时数据库之python用sqlite3模块操作sqlite

SQLite是一个包含在C库中的轻量级数据库.它并不需要独立的维护进程,并且允许使用非标准变体(nonstandard variant)的SQL查询语句来访问数据库. 一些应用可是使用SQLite保存内部数据.它也可以在构建应用原型的时候使用,以便于以后转移到更大型的数据库. SQLite的主要优点: 1. 一致性的文件格式: 在SQLite的官方文档中是这样解释的,我们不要将SQLite与Oracle或PostgreSQL去比较,与我们自定义格式的数据文件相比,SQLite不仅提供了很好的 移

python collections模块-标准库

参考老顽童博客,他写的很详细,例子也很容易操作和理解. 1.模块简介 collections包含了一些特殊的容器,针对Python内置的容器,例如list.dict.set和tuple,提供了另一种选择: namedtuple,可以创建包含名称的tuple: deque,类似于list的容器,可以快速的在队列头部和尾部添加.删除元素: Counter,dict的子类,计算可hash的对象: OrderedDict,dict的子类,可以记住元素的添加顺序: defaultdict,dict的子类,

(转)python collections模块详解

原文:https://www.cnblogs.com/dahu-daqing/p/7040490.html 参考老顽童博客,他写的很详细,例子也很容易操作和理解. 1.模块简介 collections包含了一些特殊的容器,针对Python内置的容器,例如list.dict.set和tuple,提供了另一种选择: namedtuple,可以创建包含名称的tuple: deque,类似于list的容器,可以快速的在队列头部和尾部添加.删除元素: Counter,dict的子类,计算可hash的对象: