数据库乱码问题Python编码问题(Unicode的encode、decode相互转换)
前⾔:
今天有个项⽬需要合并(A合并到B),我所做的就是数据库的合并操作,其中出现的主要问题就是乱码的问题。乱码这个问题是很常见的问题,今天整理了下⾃⼰所理解的⼀点见解。
案例:
NAME COMPANY RNAME
A the Feed Institute, CAAS tiezhengyuan
B??������??��???��
C������?��???��
D AB
E��?????
F��?����?��
G��302��?��???��??
H��?����
I��
查看表的时候都是乱码,根本不能进⾏合并。当时就认为肯定是⾃⼰查看时候的字符集不对,试了“set names = gbk、utf8、latin1”,均显⽰乱码。再怀疑是不是表结构的问题(根本不可能啊,项⽬都跑了好⼏年了),不过表的编码确实的⽤charset latin1 来建⽴的。⽽⽤lantin1 建⽴表,在数据库层写⼊数据(中⽂)的话,肯定会报warning,不会存成功的⽽且存的都是?符号,但上⾯的结果却有� 符号,所以是显⽰的问题,其实是正常的。经过排查确定是⾃⼰终端编码显⽰的问题,改成GBK,并且做默认的字符集连接下才能正常显⽰(latin1:set names latin1),因为当时存数据就是做latin1的下⾯写⼊的。上⾯说明表中的⽂字是GBK编码。
注意:latin1可以存储任何东西,包括汉字,⼆进制等。latin1是单字节的,存储都会⽤内部的编码去表⽰。只要输⼊流和输出流⼀致(怎么存就怎么取)就不会出现乱码情况。如:
表的编码是UTF8 ,那么在进⼊数据库后,需要在utf8的字符集下才能正常(set names utf8)
表的编码是GBK ,那么在进⼊数据库后,需要在gbk的字符集下才能正常(set names gbk)
⾸先看到表的字符集编码是什么,才能相应的set names。要是输⼊结果像上⾯显⽰的这样,就和终端的编码有关系了,需要⾃⼰设置下。
如何让表字符编码变成gbk呢?单单的alter 转换是不⾏的。之后就尝试⽤python来读取插⼊表。
看看latin1编码如何存汉字:(Python)
#!/usr/bin/python
#encoding: utf-8
#-------------------------------------------------------------------------------
# Name:        latin_to_gbk_bak.py
# Purpose:    gbk 编码存到 latin1编码
# Author:      zhoujy
# Created:    2012-10-31
# update:      2012-10-31
#-------------------------------------------------------------------------------
import MySQLdb
a=u'zhoujy'
b=u'中国'                        /*⽣成unicode */
c=u'周⾦义'
conn = t(host='localhost',user='root',passwd='123456',db='test')
unicode编码转换二进制cursor = conn.cursor()
/*编码成gbk存到latin1 的表中*/
query = "insert into users_latin(username,user_company,user_realname)    values('%s','%s','%s')" %(a.encode('gbk'),b.encode('gbk'),c.encode('gbk')) ute(query)
connmit()
print query
root@localhost : test 10:55:10>CREATE TABLE `users_latin` (
->  `username` varchar(25) NOT NULL DEFAULT '',
->  `user_realname` varchar(50) NOT NULL DEFAULT '',
->  `user_company` varchar(100) NOT NULL DEFAULT ''
-> ) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
Query OK, 0 rows affected (0.07 sec)
zhoujy@zhoujy:~$ python latin_to_gbk_bak.py  1
insert into users_latin(username,user_company,user_realname) values('zhoujy','�й�','����')
zhoujy@zhoujy:~$ python latin_to_gbk_bak.py  2
insert into users_latin(username,user_company,user_realname) values('zhoujy','中国','周⾦义')
root@localhost : test 10:55:14>select * from users_latin;  1
+----------+---------------+--------------+
| username | user_realname | user_company |
+----------+---------------+--------------+
| zhoujy  | ����        | �й�        |
+----------+---------------+--------------+
1 row in set (0.00 sec)
root@localhost : test 10:56:06>select * from users_latin;  2
+----------+---------------+--------------+
| username | user_realname | user_company |
+----------+---------------+--------------+
| zhoujy  | 周⾦义        | 中国        |
+----------+---------------+--------------+
1 row in set (0.00 sec)
上⾯的信息说明:latin1确实可以存中⽂。(在set names latin1下⾯读的)
<;脚本>向latin1编码的表中插⼊数据,执⾏脚本,第⼀次显⽰乱码,第⼆次显⽰正常,他们的区别是输出流是gbk,⽽系统默认/终端默认的是utf8,所以出现不同的结果。(第⼆次把系统默认/终端默认编码改成了gbk)
<;数据表> 表的编码是latin1,并且当时的字符集是latin1(set names latin1,要和表编码⼀致)。第⼀次显⽰乱码,第⼆次显⽰正常,他们的区别是输出流是gbk,⽽系统默认/终端默认的是utf8,所以出现不同的结果。(第⼆次把系统默认/终端默认编码改成了gbk)
其实也可以把latin1存储汉字的表备份出来,⽂件中要是最新字符集都是有效的,可以⽤vi可以直接编辑查看。要是出现⽆效的编码则也可以⽤: iconv 来转码之后再还原。
cat users.sql | iconv -c -f gbk  > users_qq.sql
当然,还原之后也是latin1 字符集下查看的。
解析:
1,Mysql数据库乱码:这个可以做⽹上搜到很多信息,就不细说了。只要知道怎么存就怎么取。(有时候set names utf8并且终端也是utf8 可以直接对gbk编码的表进⾏读取)
2,Python 编码问题,涉及到Unicode 的 encode、decode 相互转换。(可能做执⾏中出:“UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)” 的错误信息。)
知识点:
decode的作⽤是将其他编码的字符串转换成unicode编码,如str1.decode('gb2312'),表⽰将gb2312编码的字符串转换成unicode编码。
encode的作⽤是将unicode编码转换成其他编码的字符串,如de('gb2312'),表⽰将unicode编码的字符串转换成gb2312编码。
字符串在Python内部的表⽰是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另⼀种编码。
测试:
系统默认编码是UTF8,⽣成⼀个⽂件,⽤GBK编码⽣成中⽂。
zhoujy@zhoujy:~$
���
>>> f=open('')
>>> ad()
>>> print d
���
>>> print d.decode('gbk') #转换成了unicode
周⾦义
>>> print d.decode('gbk').encode('utf8')
周⾦义
从上⾯的信息得到:当读出⽂本时候还是乱码,因为cat 默认就是按照⽂本的编码读出,python中的read也是⼀样。但做python中默认的是unicode编码。按照知识点理的说明需要转换成为unicode才能正常显⽰。最后还需要把unicode转换成系统默认的⽂件才⾏。验证:
>>> f=open('')
>>> ad()
>>> gbk=a.decode('gbk')      /* 将gbk编码的字符串转换成unicode编码*/
>>> gbk
u'\u5468\u91d1\u4e49\n'    /*u开头的unicode编码*/
>>> print gbk
周⾦义                      /*python⾥⾯正常显⽰,因为默认就是unicode编码*/
>>> f=open('','w')
>>> f.write(gbk)            /*写⽂件*/
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)  /*字符集的问题报错*/
>>> utf=a.decode('gbk').encode('utf8')  /* 将gbk编码的字符串转换成unicode编码*,再转换成utf8格式,unicode当成中间编码*/
>>> utf
'\xe5\x91\xa8\xe9\x87\x91\xe4\xb9\x89\n' /*utf8编码显⽰*/
>>> print utf
周⾦义
>>> f=open('','w')
>>> f.write(utf)            /*写⽂件*/
>>>
zhoujy@zhoujy:~$   /*和第上⾯的⽐下,看到正常的⽂字,这是控制台信息输出窗⼝按照ascii编码输出utf8编码的字符串的结果。*/
周⾦义
所以存到⽂件最后以系统默认的编码存才能存的进去。
open 按照固定字符集打开:
>>> import codecs
>>> f=codecs.open('',encoding='gbk')
>>> ad()
>>> x                                          /* 节省了 decode的步骤 */
u'\u5468\u91d1\u4e49\n'
>>> x.encode('utf8')
'\xe5\x91\xa8\xe9\x87\x91\xe4\xb9\x89\n'
⼀个函数:isinstance()
>>> a='周⾦义'
>>> u=u'周⾦义'
>>> isinstance(a,unicode)  /*判断是否是unicode编码*/
False
>>> isinstance(u,unicode)
True
回归:
了解了这些之后,那对数据库表的导出脚本:conn.character_set_name() 是显⽰数据库的链接字符集,可以print出来,例⼦理显⽰为lanti1 #!/usr/bin/python
#encoding: utf-8
#-------------------------------------------------------------------------------
# Name:        latin_to_gbk.py
# Purpose:    latin1 编码转传成 gbk
# Author:      zhoujy
# Created:    2012-10-31
# update:      2012-10-31
#-------------------------------------------------------------------------------
import MySQLdb
conn = t(host='localhost',user='root',passwd='123456',db='phpbb')
cursor = conn.cursor()
query = "select username,user_company,user_realname from users limit 20"
#query = "select post_id,post_subject,post_text from posts_text"
connmit()
items = cursor.fetchall()
for line in items:
a,b,c = line
a=a.decode('gbk')
b=b.decode('gbk')
c=c.decode('gbk')
#    de('utf-8'),b.encode('utf-8'),c.encode('utf-8')
print a,',',b,',',c
结果:
NAME COMPANY RNAME
A the Feed Institute, CAAS tiezhengyuan
B湖北省武汉市园⼩⼆
C北京公司⼤三
D AB李逵
E上海公司宋江
F中国公司花荣
G我302从武松
H上海潘安
I中国刘备
转换:latin1 表转换到 gbk编码的表:原来表本⾝就存在乱码(不可逆),错误了继续执⾏:
#!/usr/bin/python
#encoding: utf-8
#-------------------------------------------------------------------------------
# Name:        latin_to_gbk.py
# Purpose:    latin1 编码表转传成gbk编码表
# Author:      zhoujy
# Created:    2012-10-31
# update:      2012-10-31
#-------------------------------------------------------------------------------
import MySQLdb
MySQLdb.escape_string
def convert_code(s):
#转换
if s == None or s == '':
s = ''
return MySQLdb.escape_string(str(s).decode('gbk').encode('gbk'))
else:
return MySQLdb.escape_string(str(s).decode('gbk').encode('gbk'))
def get_data(conn):
query ='''
select user_id,username from users
'''
cursor = conn.cursor()
items = cursor.fetchall()
return items
def insert_data(tconn,items):
for line in items:
a,b = line
#异常处理,错误继续,增加容错,否则错误就退出了。
try:
query = '''
insert into php_users(user_id,username)  values(%s,"%s")
''' %(a,convert_code(b))
#            print query
cursor = tconn.cursor()
tconnmit()
except Exception,e:
print "userId : " + str(a)
print e
continue
if __name__ =='__main__':
conn = t(host='192.168.1.10',user='root',passwd='123456',db='bb')
tconn = t(host='192.168.1.20',user='root',passwd='123456',db='cc')
items = get_data(conn)
insert_data(tconn,items)
因此,对于数据库⽅⾯来讲,遇到乱码时,先要核对set names⾥的三个参数是什么,再看终端使⽤什么编码。都⼀致的话⼤部分的情况不会乱码,不能随便⽤alter table convert to 进⾏转码,转码的时候⼀定要先搞明⽩,字符串str是什么编码(程序转换字符存表的的时候使⽤的是什么),然后通过脚本(python 中先把 decode成unicode,然后再encode成其他编码)进⾏解码,编码。
关于python 的encode与decode 下⾯有更多信息:
值得⼀看
关于MySQL乱码:
blog.csdn/yah99_wolf/article/details/7391089