5

argparse用来读取我的 python 代码的参数。其中一个输入是title可以包含 Unicode 字符的文件 [ ] 的标题。我一直在22少女時代22用作测试字符串。

我需要将输入的值写入title文件,但是当我尝试将字符串转换为UTF-8它时,总是会引发错误:

UnicodeDecodeError:“ascii”编解码器无法解码位置 2 中的字节 0x8f:序数不在范围内(128)

我一直在环顾四周,发现我需要我的字符串以u"foo"调用.encode()它的形式出现。

当我运行type()我的输入时,argparse我看到:

<type 'str'>

我希望得到以下答复:

<type 'unicode'>

我怎样才能以正确的形式获得它?

主意:

修改argparse以接受 astr但将其存储为 unicode 字符串u"foo"

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

这种方法根本行不通。想法?

编辑1:

一些示例代码在title哪里22少女時代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
4

3 回答 3

12

看起来您的输入数据采用SJIS 编码(日语的传统编码),它在字节串的第 2 位产生字节 0x8f:

>>> '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 中的设计缺陷,字节串可以直接encoded,并且假定字节串是 ASCII 编码的。所以你所拥有的在概念上等同于

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

当然decode通话失败。

您应该在编码为 UTF-8 之前从 SJIS 显式解码为 Unicode 字符串:

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

正如 Mark Tolonen 指出的那样,您可能正在将字符输入控制台,而您的控制台编码是非 Unicode 编码。

所以事实证明你的sys.stdin.encodingis cp932,它是微软的 SJIS 变体。为此,使用

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

您确实应该将控制台编码设置为标准 UTF-8,但我不确定这在 Windows 上是否可行。如果这样做,您可以跳过解码/编码步骤,只需将输入字节串写入文件。

于 2012-10-06T23:04:53.460 回答
4

设置type=unicode就像在 Python 2.X 上使用unicode(arg)默认解码。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 程序使用的当前编码。这似乎是argparse正在使用的,因为我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 文件的开头写入 Windows 喜欢的utf-8-sig所谓字节顺序标记 (BOM)。 utf-8如果不需要,可以使用,但记事本喜欢它。

于 2012-10-06T23:17:44.313 回答
3

所以,这实际上对我有用:

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 Terminal.app)使用 UTF-8。如果您的终端未配置为 UTF-8 操作,那么它将无法工作(然后这是终端问题,而不是 Python 问题)。

于 2012-10-06T23:01:21.343 回答