問題描述
在python 2.7中,使用subprocess.Popen()調用*nix命令,並通過管道,獲取其輸出,並將其返回值格式化成utf-8格式,但是對於返回值出現中文時,會造成轉碼錯誤,具體情況如下:
# -*- coding: utf-8 -*-
import subprocess
MY_RPM_FILE_PATH = '/home/dongliang.ma/rpmbuild/RPMS/noarch/q-yumtools-server-1.1.0-1.el6.noarch.rpm'
try:
cmd_args = ['rpm', '-qip', MY_RPM_FILE_PATH]
infos = subprocess.Popen(cmd_args, stdout=subprocess.PIPE, shell=False).stdout.readlines()
unicode_text = u''.join(infos)
print unicode_text
except Exception, _ex:
print 'ERROR: %s' % str(_ex)
問題分析
從python的報錯來看,是中文編碼超出了ASCII碼的表示范圍,具體報錯的代碼為:
unicode_text = u''.join(infos)
既然是無法表示,我的第一反應就是進行轉碼,於是將代碼改寫:
# -*- coding: utf-8 -*-
import subprocess
MY_RPM_FILE_PATH = '/home/dongliang.ma/rpmbuild/RPMS/noarch/q-yumtools-server-1.1.0-1.el6.noarch.rpm'
try:
cmd_args = ['rpm', '-qip', MY_RPM_FILE_PATH]
infos = subprocess.Popen(cmd_args, stdout=subprocess.PIPE, shell=False).stdout.readlines()
unicode_text = u''
for line in infos:
unicode_text = unicode_text + line.encode('utf-8')
print unicode_text
except Exception, _ex:
print 'ERROR: %s' % str(_ex)
但是經過轉換後,代碼運行報錯和先前一樣,經過分析,在執行下面代碼時,問題就已經發生:
infos = subprocess.Popen(cmd_args, stdout=subprocess.PIPE, shell=False).stdout.readlines()
那麼現在問題就定位到如何讓infos這個變量能夠存儲中文,經過查閱文檔,發現python有一個defaultencoding的概念,即默認使用的編碼,只要將這個屬性設置為utf-8,那麼就可以正確存儲中文,其具體設置方法是使用:
reload(sys)
sys.setdefaultencoding('utf-8')
經過上述設置後,即可正確處理中文信息。
最終方案
# -*- coding: utf-8 -*-
import sys
import subprocess
MY_RPM_FILE_PATH = '/home/dongliang.ma/rpmbuild/RPMS/noarch/q-yumtools-server-1.1.0-1.el6.noarch.rpm'
try:
reload(sys)
sys.setdefaultencoding('utf-8')
cmd_args = ['rpm', '-qip', MY_RPM_FILE_PATH]
infos = subprocess.Popen(cmd_args, stdout=subprocess.PIPE, shell=False).stdout.readlines()
unicode_text = u''.join(infos)
print unicode_text
except Exception, _ex:
print 'ERROR: %s' % str(_ex)
運行結果:
總結
python默認使用的編碼是ASCII,這會導致國際化的問題,一個比較好的做法是,在程序運行時,將默認編碼修改為utf-8;
在python源文件中使用unicode字符時,一定要將python文件保存為utf-8格式,而不是僅僅在開頭加上# -*- coding: utf-8 -*-這個bug,導致我調試發送郵件時浪費了不少時間。