轉 python3中SQLLIT編碼與解碼之Unicode與bytes


 #########sample##########

sqlite3.OperationalError: Could not decode to UTF-8 column 'logtype' with text


with connection.cursor() as c:
c.execute("select id,name from district_info where p_id=0")
provinces = c.fetchall()

 

調整為

con = sqlite3.connect('./db.sqlite3')
# con.text_factory = bytes
con.text_factory = lambda x: str(x, 'gbk', 'ignore')
cur = con.cursor()
# with connection.cursor() as c:
c=cur.execute("select id,name from district_info where p_id=0")
provinces = c.fetchall()
return JsonResponse(provinces, safe=False)

 

 

############################

https://docs.python.org/3/library/sqlite3.html?highlight=conn%20text_factory%20str
https://docs.python.org/3/library/sqlite3.html?highlight=conn%20text_factory%20str
https://blog.csdn.net/chb4715/article/details/79104299 ( python3中編碼與解碼之Unicode與bytes)


https://www.cnblogs.com/lightwind/p/4499193.html (重要,python3中SQLLIT編碼與解碼之Unicode與bytes)

 

寫這篇文章,起源於要寫一個腳本批量把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)
復制代碼

 

打印結果:

1
2
3
4
5
6
7
8
9
10
11
12
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編碼時,產生的結果,更亂,不推薦。

1
2
3
4
5
6
7
8
9
10
11
12
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' ,)]

  

 

##################

https://blog.csdn.net/xkxjy/article/details/8179479

 

環境:python3.2 sqllite3
代碼如下:
import sqlite3 as sql

conn = sql.connect(r'c:\setupinfidb.db', detect_types=sql.PARSE_COLNAMES)
c = conn.cursor()
c.execute('select * from setuplog')
for row in c:
 print(row)

運行以上代碼時,提示:
Traceback (most recent call last):
  File "sqlitetest.py", line x, in <module>
    c.execute('select * from setuplog')
sqlite3.OperationalError: Could not decode to UTF-8 column 'logtype' with text '\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd'

這個意思是說column ‘logtype’ 不能通過UTF-8 decode,就是logtype不是用utf8編碼的
一般情況下這個情況出現在text類型的數據上面
這個可以通過設置 conn.text_factory 解決
如 conn.text_factory = bytes
把text類型當bytes來解釋,就不會出錯了
不過,這樣也不太好,如果知道是什么編碼就好了,例子代碼是gbk編碼的
這里可以這樣設置:
conn.text_factory = lambda x : str(x, 'gbk', 'ignore')
指示以gbk來解碼而不是默認的utf8
---------------------
作者:xkxjy
來源:CSDN
原文:https://blog.csdn.net/xkxjy/article/details/8179479
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

 

#####################


python3中編碼與解碼之Unicode與bytes

 

今天玩Python爬蟲,下載一個網頁,然后把所有內容寫入一個txt文件中,出現錯誤;

TypeError: write() argument must be str, not bytes

AttributeError: 'URLError' object has no attribute 'code'

UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' inposition 5747: illegal multibyte sequence

一看就是編碼問題,不懂,度娘上面這方面講得不多,感覺沒說清楚,自己研究了一晚上,摸出了一點門道。

從頭說起,由於各國語言文字不同,起初要在計算機中表示,就有了各種各樣的編碼(例如中文的gb2312)。但是這樣就出現了兼容性的問題,所以就有了Unicode,也就是所謂的萬國碼,python3中字符串類型str就是以Unicode編碼格式編碼,所以我們在Python3 中看到多種語言文字的字符串而不會出現亂碼。

編碼是一種用一種特定的方式對抽象字符(Unicode)轉換為二進制形式(bytes)進行表示,也就是python3中的encode。解碼就是對用特定方式表示的二進制數據用特定的方式轉化為Unicode,也就是decode。

下圖就是編碼的核心:

一、字符的編碼:

Python對於bites類型的數據用帶‘b‘前綴的單引號活雙引號表示。

下面關於字符編碼解碼的代碼很好的解釋了上面的流程圖:

s='你好'
print(s)#輸出結果:你好
print(type(s))#輸出結果:<class 'str'>
s=s.encode('UTF-8')
print(s)#輸出結果:b'\xe4\xbd\xa0\xe5\xa5\xbd'
print(type(s))#輸出結果:<class 'bytes'>
s=s.decode('UTF-8')
print(s)#輸出結果:你好
print(type(s))#輸出結果:<class 'str'>

多說一句,如果你對str類型字符進行decode會報錯,同理,對bytes類型進行encode也會報錯。

二、文件編碼

在python 3 中字符是以Unicode的形式存儲的,當然這里所說的存儲是指存儲在計算機內存當中,如果是存儲在硬盤里,Python 3的字符是以bytes形式存儲,也就是說如果要將字符寫入硬盤,就必須對字符進行encode。對上面這段話再解釋一下,如果要將str寫入文件,如果以‘w’模式寫入,則要求寫入的內容必須是str類型;如果以‘wb’形式寫入,則要求寫入的內容必須是bytes類型。文章開頭出現的集中錯誤,就是因為寫入模式與寫入內容的數據類型不匹配造成的。

s1 = '你好'
#如果是以‘w’的方式寫入,寫入前一定要進行encoding,否則會報錯
with open('F:\\1.txt','w',encoding='utf-8') as f1:
    f1.write(s1)
s2 = s1.encode("utf-8")#轉換為bytes的形式
#這時候寫入方式一定要是‘wb’,且一定不能加encoding參數
with open('F:\\2.txt','wb') as f2:
    f2.write(s2)
有的人會問,我在系統里面用文本編輯器打開以bytes形式寫入的2.txt文件,發現里面顯示的是‘你好’,而不是‘b'\xe4\xbd\xa0\xe5\xa5\xbd'’,因為文本文檔打開2.txt時,又會對它進行decode,然后才給你看到。

三、網頁的編碼

網頁編碼和文件編碼方法差不多,如下urlopen下載下來的網頁read()且用decoding(‘utf-8’)解碼,那就必須以‘w’的方式寫入文件。如果只是read()而不用encoding(‘utf-8’)進行編碼,一定要以‘wb’方式寫入:

以‘w’方式寫入時:

response= url_open('http://blog.csdn.net/gs_zhaoyang/article/details/13768925 ' ,timeout=5 )
#此處以UTF-8方式進行解碼,解碼后的數據以unicode的方式存儲在html中
html = response.read().decode('UTF-8')
print(type(html))#輸出結果:<class 'str'>
#這時寫入方式一定要加encoding,以encoding
# 即UTF-8的方式對二進制數據進行編碼才能寫入
with open('F:\DownloadAppData\html.txt',"w" , encoding='UTF-8') as f:
    f.write(html)
以‘wb’方式寫入:

response= url_open('http://blog.csdn.net/gs_zhaoyang/article/details/13768925 ' ,timeout=5 )
html = response.read()#此處不需要進行解碼,下載下來
print(type(html))#輸出結果:<class 'bytes'>
with open('F:\DownloadAppData\html.txt',"wb" ) as f:
    f.write(html)
如果要在Python3中,對urlopen下來的網頁進行字符搜索,肯定也要進行decode,例如使用lxml.etree就必須進行decode。

 
---------------------
作者:奧辰_
來源:CSDN
原文:https://blog.csdn.net/chb4715/article/details/79104299
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

 

 

 

###################

http://www.runoob.com/python/python-func-str.html (重要,Python str() 函數)
https://www.cnblogs.com/sesshoumaru/p/6070713.html (Python str() 函數))

http://www.runoob.com/python3/python3-string-encode.html (Python3 encode()方法)

 

class str(object=''class

str(object=b''encoding='utf-8'errors='strict')

Return a string version of object. If object is not provided, returns the empty string. Otherwise, the behavior of str()depends on whether encoding or errors is given, as follows.

If neither encoding nor errors is given, str(object) returns object.__str__(), which is the “informal” or nicely printable string representation of object. For string objects, this is the string itself. If object does not have a __str__() method, then str() falls back to returning repr(object).

If at least one of encoding or errors is given, object should be a bytes-like object (e.g. bytes or bytearray). In this case, if object is a bytes (or bytearray) object, then str(bytes, encoding, errors) is equivalent to bytes.decode(encoding, errors). Otherwise, the bytes object underlying the buffer object is obtained before calling bytes.decode(). See Binary Sequence Types — bytes, bytearray, memoryview and Buffer Protocol for information on buffer objects.

 

說明:

  1. str函數功能時將對象轉換成其字符串表現形式,如果不傳入參數,將返回空字符串。

復制代碼
>>> str()
''
>>> str(None)
'None'
>>> str('abc')
'abc'
>>> str(123)
'123'
復制代碼

  2. 當轉換二進制流時,可以傳入參數encoding,表示讀取字節數組所使用的編碼格式;參數errors,表示讀取二進制的錯誤級別。(這兩個參數和open方法中的同名參數有相同取值和類似的含義,詳見Python內置函數(47)——open)。

復制代碼
>>> file = open('test.txt','rb') # 打開文件
>>> fileBytes = file.read() # 讀取二進制流
>>> fileBytes
b'\xe6\x88\x91\xe6\x98\xaf\xe7\xac\xac1\xe8\xa1\x8c\xe6\x96\x87\xe6\x9c\xac\xef\xbc\x8c\xe6\x88\x91\xe5\xb0\x86\xe8\xa2\xab\xe6\x98\xbe\xe7\xa4\xba\xe5\x9c\xa8\xe5\xb1\x8f\xe5\xb9\x95\r\n\xe6\x88\x91\xe6\x98\xaf\xe7\xac\xac2\xe8\xa1\x8c\xe6\x96\x87\xe6\x9c\xac\xef\xbc\x8c\xe6\x88\x91\xe5\xb0\x86\xe8\xa2\xab\xe6\x98\xbe\xe7\xa4\xba\xe5\x9c\xa8\xe5\xb1\x8f\xe5\xb9\x95\r\n\xe6\x88\x91\xe6\x98\xaf\xe7\xac\xac3\xe8\xa1\x8c\xe6\x96\x87\xe6\x9c\xac\xef\xbc\x8cr\xe6\x88\x91\xe5\xb0\x86\xe8\xa2\xab\xe6\x98\xbe\xe7\xa4\xba\xe5\x9c\xa8\xe5\xb1\x8f\xe5\xb9\x95'
>>> str(fileBytes) # 默認將二進制流轉換成字符串表現形式
"b'\\xe6\\x88\\x91\\xe6\\x98\\xaf\\xe7\\xac\\xac1\\xe8\\xa1\\x8c\\xe6\\x96\\x87\\xe6\\x9c\\xac\\xef\\xbc\\x8c\\xe6\\x88\\x91\\xe5\\xb0\\x86\\xe8\\xa2\\xab\\xe6\\x98\\xbe\\xe7\\xa4\\xba\\xe5\\x9c\\xa8\\xe5\\xb1\\x8f\\xe5\\xb9\\x95\\r\\n\\xe6\\x88\\x91\\xe6\\x98\\xaf\\xe7\\xac\\xac2\\xe8\\xa1\\x8c\\xe6\\x96\\x87\\xe6\\x9c\\xac\\xef\\xbc\\x8c\\xe6\\x88\\x91\\xe5\\xb0\\x86\\xe8\\xa2\\xab\\xe6\\x98\\xbe\\xe7\\xa4\\xba\\xe5\\x9c\\xa8\\xe5\\xb1\\x8f\\xe5\\xb9\\x95\\r\\n\\xe6\\x88\\x91\\xe6\\x98\\xaf\\xe7\\xac\\xac3\\xe8\\xa1\\x8c\\xe6\\x96\\x87\\xe6\\x9c\\xac\\xef\\xbc\\x8cr\\xe6\\x88\\x91\\xe5\\xb0\\x86\\xe8\\xa2\\xab\\xe6\\x98\\xbe\\xe7\\xa4\\xba\\xe5\\x9c\\xa8\\xe5\\xb1\\x8f\\xe5\\xb9\\x95'"
>>> str(fileBytes,'utf-8') # 傳入encoding參數,函數將以此編碼讀取二進制流的內容
'我是第1行文本,我將被顯示在屏幕\r\n我是第2行文本,我將被顯示在屏幕\r\n我是第3行文本,r我將被顯示在屏幕'
>>> str(fileBytes,'gbk') # 當傳入encoding不能解碼時,會報錯(即errors參數默認為strict)
Traceback (most recent call last):
  File "<pyshell#46>", line 1, in <module>
    str(fileBytes,'gbk')
UnicodeDecodeError: 'gbk' codec can't decode byte 0xac in position 8: illegal multibyte sequence
>>> str(fileBytes,'gbk','ignore') # 'ignore' 忽略級別,字符編碼有錯,忽略掉.
'鎴戞槸絎1琛屾枃鏈錛屾垜灝嗚鏄劇ず鍦ㄥ睆騫\r\n鎴戞槸絎2琛屾枃鏈錛屾垜灝嗚鏄劇ず鍦ㄥ睆騫\r\n鎴戞槸絎3琛屾枃鏈錛宺鎴戝皢琚鏄劇ず鍦ㄥ睆騫'
>>> str(fileBytes,'gbk','replace') # 'replace' 替換級別,字符編碼有錯的,替換成?. 
'鎴戞槸絎�1琛屾枃鏈�錛屾垜灝嗚��鏄劇ず鍦ㄥ睆騫�\r\n鎴戞槸絎�2琛屾枃鏈�錛屾垜灝嗚��鏄劇ず鍦ㄥ睆騫�\r\n鎴戞槸絎�3琛屾枃鏈�錛宺鎴戝皢琚�鏄劇ず鍦ㄥ睆騫�'
復制代碼
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM