2012-10-06 95 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