前言:
今天有個項目需要合並(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 = MySQLdb.connect(host='localhost',user='root',passwd='123456',db='test')
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'))
cursor.execute(query)
conn.commit()
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編碼轉換成其他編碼的字符串,如str2.encode('gb2312'),表示將unicode編碼的字符串轉換成gb2312編碼。
字符串在Python內部的表示是unicode編碼,因此,在做編碼轉換時,通常需要以unicode作為中間編碼,即先將其他編碼的字符串解碼(decode)成unicode,再從unicode編碼(encode)成另一種編碼。
測試:
系統默認編碼是UTF8,生成一個文件,用GBK編碼生成中文。
zhoujy@zhoujy:~$ cat gbk.txt
�ܽ��
>>> f=open('gbk.txt')
>>> d=f.read()
>>> print d
�ܽ��
>>> print d.decode('gbk') #轉換成了unicode
周金義
>>> print d.decode('gbk').encode('utf8')
周金義
從上面的信息得到:當讀出文本時候還是亂碼,因為cat 默認就是按照文本的編碼讀出,python中的read也是一樣。但做python中默認的是unicode編碼。按照知識點理的說明需要轉換成為unicode才能正常顯示。最后還需要把unicode轉換成系統默認的文件才行。驗證:
>>> f=open('gbk.txt')
>>> a=f.read()
>>> gbk=a.decode('gbk') /* 將gbk編碼的字符串轉換成unicode編碼*/
>>> gbk
u'\u5468\u91d1\u4e49\n' /*u開頭的unicode編碼*/
>>> print gbk
周金義 /*python里面正常顯示,因為默認就是unicode編碼*/
>>> f=open('gbk.txt','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('gbk.txt','w')
>>> f.write(utf) /*寫文件*/
>>>
zhoujy@zhoujy:~$ cat gbk.txt /*和第上面的比下,看到正常的文字,這是控制台信息輸出窗口按照ascii編碼輸出utf8編碼的字符串的結果。*/
周金義
所以存到文件最后以系統默認的編碼存才能存的進去。
open 按照固定字符集打開:
>>> import codecs >>> f=codecs.open('gbk.txt',encoding='gbk')
>>> x=f.read()
>>> 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 = MySQLdb.connect(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"
cursor.execute(query)
conn.commit()
items = cursor.fetchall()
for line in items:
a,b,c = line
a=a.decode('gbk')
b=b.decode('gbk')
c=c.decode('gbk')
# print a.encode('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()
cursor.execute(query)
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()
cursor.execute('set names gbk')
cursor.execute(query)
tconn.commit()
except Exception,e:
print "userId : " + str(a)
print e
continue
if __name__ =='__main__':
conn = MySQLdb.connect(host='192.168.1.10',user='root',passwd='123456',db='bb')
tconn = MySQLdb.connect(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 下面有更多信息:
http://cenalulu.github.io/mysql/mysql-mojibake 值得一看
http://blog.csdn.net/lf8289/article/details/2465196
http://blog.csdn.net/lxdcyh/article/details/4018054
http://www.cnblogs.com/evening/archive/2012/04/19/2457440.html
關於MySQL亂碼:
http://blog.csdn.net/yah99_wolf/article/details/7391089