2012-10-06 154 views
5

我使用​​來讀取我的Python代碼的參數。其中一個輸入是可包含Unicode字符的文件標題[title]。我一直在使用22少女時代22作爲測試字符串。Python的Unicode編碼

我需要輸入title的值寫入文件,但是當我嘗試將字符串轉換爲UTF-8它總是拋出一個錯誤:

UnicodeDecodeError: 'ascii' codec can't decode byte 0x8f in position 2: ordinal not in range(128)

我一直環顧四周,看到我需要我的字符串將以u"foo"的形式在其上調用.encode()

當我在我的輸入運行type()從​​我看到:

<type 'str'> 

我希望得到的響應:

<type 'unicode'> 

我怎樣才能得到它在正確的形式?

理念:

修改​​採取一個str,但其存儲爲Unicode字符串u"foo"

parser.add_argument(u'title', metavar='T', type=unicode, help='this will be unicode encoded.') 

這種做法是行不通的。思考?

編輯1:

一些示例代碼,其中title22少女時代22

inputs = vars(parser.parse_args()) 
title = inputs["title"] 
print type(title) 
print type(u'foo') 
title = title.encode('utf8') # This line throws the error 
print title 
+0

輸入數據是什麼編碼? –

+0

@MarkTolonen好的,我會編輯我的帖子。 – Morrowind789

回答

12

它看起來像輸入數據是在SJIS encoding(日本遺留編碼),這在字節串產生字節值爲0x8F在位置2:

>>> '22少女時代22'.encode('sjis') 
b'22\x8f\xad\x8f\x97\x8e\x9e\x91\xe322' 

(在Python 3的提示)

現在,我猜爲「字符串轉換爲UTF-8」,你使用像

title.encode('utf8') 

的問題是, title實際上是一個包含SJIS編碼字符串的字節串。由於Python 2中的設計缺陷,字節串可以直接使用encode d,並假定字符串是ASCII編碼的。所以你在概念上等同於

title.decode('ascii').encode('utf8') 

當然,decode呼叫失敗。

而應該明確地從SJIS解碼爲Unicode字符串,編碼前爲UTF-8:

title.decode('sjis').encode('utf8') 

正如馬克Tolonen指出的那樣,你可能鍵入字符到您的控制檯,這是你的控制檯編碼是一個非Unicode編碼。

因此,事實證明你的sys.stdin.encodingcp932,這是微軟的SJIS變種。對於這一點,使用

title.decode('cp932').encode('utf8') 

你真的應該控制檯編碼設置爲標準UTF-8,但我不知道這是可能在Windows上。如果這樣做,您可以跳過解碼/編碼步驟,並將輸入字符串寫入文件。

+0

OP可以執行'import sys;在控制檯打印sys.stdin.encoding'以確定輸入編碼,或者只使用'title.decode(sys.stdin.encoding)'。 –

+0

在Python 2.7.2上,我從'print sys.stdin.encoding;'收到了'cp932';' – Morrowind789

+0

@Mechanicalsnail嗯。調用'print title.decode('cp932')。encode('utf8')'打印'22這是輸入字符串的一個奇怪的變化。思考? – Morrowind789

2

所以,這實際上對我的作品:

import argparse 
parser = argparse.ArgumentParser() 
parser.add_argument(u'title', metavar='T', type=str, help='this will be unicode encoded.') 
opts = parser.parse_args() 
print opts.title.decode('utf8') 

我的終端仿真程序(OS X終端.app)使用UTF-8。如果你的終端沒有配置爲UTF-8操作,那麼它將不起作用(然後它是一個終端問題,而不是Python問題)。

+0

嗯有趣。讓我重新檢查我的環境。我正在使用2.7.2。 – Morrowind789

4

設置type=unicode就像使用unicode(arg),默認爲在Python 2.X上使用ascii進行解碼。如果從控制檯運行,sys.stdin.encoding是用於輸入的編碼,所以像:

inputs = vars(parser.parse_args()) 
title = inputs["title"] 
print type(title) 
print type(u'foo') 
title = title.decode(sys.stdin.encoding) 
print title 

東西應該工作無論在Windows上的編碼是mbcs編碼,它代表了非Unicode當前使用的編碼Windows程序。這似乎是​​正在使用什麼,因爲我sys.stdin.encodingOEM控制檯編碼,它不總是與Windows編碼相同。在美國的Windows,cp437是控制檯OEM編碼和cp1252是Windows編碼:

import argparse 
import codecs 
parser = argparse.ArgumentParser() 
parser.add_argument(u'title', metavar='T', type=str, help='this will be unicode encoded.') 
opts = parser.parse_args() 
title = opts.title.decode('mbcs') 
with codecs.open('out.txt','w',encoding='utf-8-sig') as f: 
    f.write(title) 

out.txt應顯示在記事本中原始輸入。

utf-8-sig編碼編寫了Windows在UTF-8文件開始時所喜歡的所謂的字節順序標記(BOM)。如果不需要,可以使用utf-8,但記事本喜歡它。

+0

您可以使用'title.decode(sys.stdin.encoding)'的好處。 –

+0

@Mechanicalsnail,原來它不適用於美國的Windows。我爲什麼添加了一個筆記。某些Windows系統對控制檯和非控制檯程序沒有相同的編碼。 –

+0

@MarkTolonen我可以證實這個作品在運行Win7 x64的我的盒子上。 [見圖片](http://i.imgur.com/Wu29q.jpg) – Morrowind789