UnicodeEncodeError: 'ascii' 编解码器无法编码第0个位置上的字符:该位置的序号超出范围(128)。

30
我正在编写一个使用剪刀字符(9986 - ✂)的Python脚本,我正在尝试将我的代码移植到Mac上,但是我遇到了这个错误。
当从IDLE运行时(Python 3.2.5 - OS X 10.4.11 iBook G4 PPC),剪刀字符显示正常,并且该代码在Ubuntu 13.10上完全正常工作。但是当我尝试在终端中运行它时,我会得到以下错误/ traceback:
Traceback (most recent call last):
  File "snippets-convert.py", line 352, in <module>
    main()
  File "snippets-convert.py", line 41, in main
    menu()
  File "snippets-convert.py", line 47, in menu
    print ("|\t ",snipper.decode(),"PySnipt'd",snipper.decode(),"\t|")
UnicodeEncodeError: 'ascii' codec can't encode character '\u2702' in position 0: ordinal not in range(128)

下面是出现问题的代码:

print ("|\t ",chr(9986),"PySnipt'd",chr(9986),"\t|")

这不是表示终端无法显示该字符吗?我知道这是一个老系统,但它目前是我唯一要使用的系统。操作系统的年龄是否会干扰程序运行?

我阅读了以下问题:

是什么原因导致这个错误?是系统/操作系统的年龄、Python版本还是某些编程错误?

编辑:此错误稍后出现了重复问题(只是想添加它,因为它在同一程序中并且是相同的错误):

Traceback (most recent call last):
  File "snippets-convert.py", line 353, in <module>
    main()
  File "snippets-convert.py", line 41, in main
    menu()
  File "snippets-convert.py", line 75, in menu
    main()
  File "snippets-convert.py", line 41, in main
    menu()
  File "snippets-convert.py", line 62, in menu
    search()
  File "snippets-convert.py", line 229, in search
    print_results(search_returned)      # Print the results for the user
  File "snippets-convert.py", line 287, in print_results
    getPath(toRead)                                             # Get the path for the snippet
  File "snippets-convert.py", line 324, in getPath
    snipXMLParse(path)
  File "snippets-convert.py", line 344, in snipXMLParse
    print (chr(164),child.text)
UnicodeEncodeError: 'ascii' codec can't encode character '\xa4' in position 0: ordinal not in range(128)

编辑:

我进入了终端字符设置,发现它确实支持该字符(如下图所示:

enter image description here

当我将其插入终端时,它输出:\342\234\202,当我按下Enter键后,它显示:-bash: ✂: command not found

编辑:按照@J.F. Sebastian的要求运行命令:

python3 test-io-encoding.py

PYTHONIOENCODING:       None
locale(False):  US-ASCII
device(stdout): US-ASCII
stdout.encoding:        US-ASCII
device(stderr): US-ASCII
stderr.encoding:        US-ASCII
device(stdin):  US-ASCII
stdin.encoding: US-ASCII
locale(False):  US-ASCII
locale(True):   US-ASCII

python3 -S test-io-encoding.py:

PYTHONIOENCODING:       None
locale(False):  US-ASCII
device(stdout): US-ASCII
stdout.encoding:        US-ASCII
device(stderr): US-ASCII
stderr.encoding:        US-ASCII
device(stdin):  US-ASCII
stdin.encoding: US-ASCII
locale(False):  US-ASCII
locale(True):   US-ASCII

编辑 尝试了@PauloBu提供的“黑客式”解决方案:

正如您所看到的,这导致了一个(好极了!)剪刀,但现在我遇到了一个新的错误。跟踪/错误:

+-=============================-+
✂Traceback (most recent call last):
  File "snippets-convert.py", line 357, in <module>
    main()
  File "snippets-convert.py", line 44, in main
    menu()
  File "snippets-convert.py", line 52, in menu
    print("|\t "+sys.stdout.buffer.write(chr(9986).encode('UTF-8'))+" PySnipt'd "+ sys.stdout.buffer.write(chr(9986).encode('UTF-8'))+" \t|")
TypeError: Can't convert 'int' object to str implicitly

编辑:添加了@PauloBu的修复结果:

+-=============================-+
|
✂ PySnipt'd 
✂       |
+-=============================-+

编辑:

他的修复方法如下:

+-=============================-+
✂✂|       PySnipt'd     |
+-=============================-+

@delnan 它返回:b'\xe2\x9c\x82' - RPiAwesomeness
当然,你需要输出字节,但我不确定如何可靠地做到这一点,而且只有在控制台实际使用UTF-8并且Python没有意识到这一点时,才能解决问题。 - user395760
这个答案是针对Python 2的,但是它可能会有所帮助:https://dev59.com/g3RB5IYBdhLWcg3w1Kv0#1169209 - Mark Ransom
@MarkRansom 是的,我看过了。如果可以的话,我计划尝试一些。 - RPiAwesomeness
@RPiAwesomeness,如果你还没解决问题,我已经编辑了我的答案并提供了更多信息,请好好尝试!祝你好运! - Paulo Bu
显示剩余2条评论
4个回答

24
当Python打印和输出内容时,它会自动编码到目标介质。如果是文件,则默认使用UTF-8,大家都会很开心。但是如果是终端,Python会找出终端正在使用的编码,并尝试使用该编码对输出进行编码。
这意味着,如果您的终端使用ascii作为编码方式,Python会尝试将scissor字符编码为ascii。当然,ascii不支持此操作,因此会出现Unicode解码错误。
这就是为什么您总是必须明确地编码您的输出。记住,显式比隐式更好?要修复您的代码,您可以执行以下操作:
import sys
sys.stdout.buffer.write(chr(9986).encode('utf8'))

这似乎有点黑客风格。在执行脚本之前,您还可以设置PYTHONIOENCODING=utf-8。我对这两种解决方案都感到不舒服。可能您的控制台不支持utf-8,因此您看到了乱码。但是您的程序将正常运行。

如果您确实需要在控制台上显示正确的输出,我强烈建议您将控制台设置为使用另一种编码,支持剪刀字符的编码(例如utf-8)。在Linux上,可以通过执行以下操作来实现: export lang=UTF_8。在Windows上,您可以使用chcp更改控制台的代码页。只需找出如何在您的控制台中设置utf8,我认为这将是最佳解决方案。


您不能混合使用printsys.stdout.write,因为它们基本上是相同的。关于您的代码,黑客方式可能是这样的:

sys.stdout.buffer.write(("|\t "+ chr(9986) +" PySnipt'd " + chr(9986)+" \t|").encode('utf8'))

我建议您阅读文档,了解print函数和sys.stdout的工作原理:http://docs.python.org/3/library/sys.html#sys.stdin。希望这能帮到您!

那很有道理。我该怎么修复它?太棒了!我会试一下这个! - RPiAwesomeness
在Python 3中,这只会输出字节,因此像这样的代码将输出字符串"b'\\xe2\\x9c\\x82'" - user395760
@PauloBu Python 最终会将字节写入控制台,但由于sys.stdout是一个TextIO对象,print将会把它的参数转换成Unicode字符串并写入该文件对象,然后该对象将会把Unicode字符串编码为底层字节流的编码并将这些字节写入流中。此外,您的示例不起作用encode始终结果是bytes,并且永远不会隐式解码,所以将其交给任何进行文本输入/输出的东西都会导致打印出字节的repr(b'...')。请记住我们正在讨论的是 Python 3 - user395760
1
“你总是必须明确地编码输出”是不正确的。您不希望使用完全重复但输出字符编码不同以便在不同环境中运行它们的脚本。 - jfs
1
我认为通用的解决方案是尝试打印Unicode并捕获任何UnicodeEncodeError异常。然后可以打印字符串的repr()。只是一个想法:) - Alastair McCormack
显示剩余19条评论

17

test_io_encoding.py 的输出表明您需要更改您的 locale 设置,例如设置为 LANG=en_US.UTF-8


第一个错误可能是因为您正在尝试解码已经是 Unicode 的字符串。Python 2 在解码之前会使用默认字符编码('ascii')对其进行编码,然后再使用(可能)不同的字符编码进行解码。错误发生在 encode 步骤中:

>>> u"\u2702".decode() # Python 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2702' in position 0: ordinal not in range(128)

看起来您正在使用Python 2而不是Python 3运行脚本。您会得到以下结果:

>>> "\u2702".decode() # Python 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'decode'

否则会产生不同的错误。

只需删除 .decode() 调用即可:

print("|\t {0} PySnipt'd {0} \t|".format(snipper))

第二个问题是由于将Unicode字符串打印到管道中:
$ python3 -c'print("\u2702")'
✂
$ python3 -c'print("\u2702")' | cat
Traceback (most recent call last):
  File "<string>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\u2702' in position 0: ordinal not in range(128)

设置适合您用途的PYTHONIOENCODING环境变量:

$ PYTHONIOENCODING=utf-8 python3 -c'print("\u2702")' | cat

终端只显示:| b'\xe2\x9c\x82' PySnipt'd b'\xe2\x9c\x82' |

如果 snipper 是一个 bytes 对象,则保留 snipper.decode() 调用。

$ python3 -c"print(b'\xe2\x9c\x82'.decode())"
✂
$ python3 -c"print(b'\xe2\x9c\x82'.decode())" | cat
Traceback (most recent call last):
  File "<string>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\u2702' in position 0: ordinal not in range(128)

这个问题的解决方法是一样的:
$ PYTHONIOENCODING=utf-8 python3 -c"print(b'\xe2\x9c\x82'.decode())" | cat

不错的答案,但它给了我与这里相同的东西:https://dev59.com/pmEi5IYBdhLWcg3w0PCQ#20923794?noredirect=1#comment31415491_20923794 - RPiAwesomeness
1
设置环境变量有点过头了。在Python2.7中,只需指定输出编码即可。我刚意识到在Python3中这不会完全相同。肯定还有另一种更简单(符合Python风格)的方法来做到这一点。 - Paulo Bu
1
@PauloBu:你想在每次在具有不同字符编码的终端中运行程序时更改源代码吗? - jfs
@J.F.Sebastian 当然没问题。我得到了这个错误信息:UnicodeEncodeError: 'ascii'编解码器无法在位置0处编码字符'\u2702':超出范围(128) - RPiAwesomeness
@RPiAwesomeness:你怎么运行它?你确定你完全复制粘贴了吗?我不相信你的终端使用ascii作为字符编码。你改变了site.pysitecustomize.pyusercustomize.py模块了吗?如果你运行以下命令,你会得到什么结果:PYTHONIOENCODING=ascii:backslashreplace python3 -c"print(b'\xe2\x9c\x82'.decode())" - jfs
显示剩余10条评论

0

我的语言环境设置为de_AT.UTF-8,但是在/etc/profile中缺少这些行:

export LANG=de_AT.UTF-8
export LANGUAGE=de_AT.UTF-8
export LC_ALL=de_AT.UTF-8

退出/登录,您的问题应该得到解决

为了验证所有区域设置是否正确,请在终端中键入locale

输出应类似于此:

LANG=de_AT.UTF-8
LANGUAGE=de_AT.UTF-8
LC_CTYPE="de_AT.UTF-8"
LC_NUMERIC="de_AT.UTF-8"
LC_TIME="de_AT.UTF-8"
LC_COLLATE="de_AT.UTF-8"
LC_MONETARY="de_AT.UTF-8"
LC_MESSAGES="de_AT.UTF-8"
LC_PAPER="de_AT.UTF-8"
LC_NAME="de_AT.UTF-8"
LC_ADDRESS="de_AT.UTF-8"
LC_TELEPHONE="de_AT.UTF-8"
LC_MEASUREMENT="de_AT.UTF-8"
LC_IDENTIFICATION="de_AT.UTF-8"
LC_ALL=de_AT.UTF-8

-4
在你的 .py 文件的第一行,你需要添加这个字符串:

# -- coding: utf-8 --

你也可以尝试这个:

print ("|\t ",unichr(9986),"PySnipt'd",unichr(9986),"\t|")


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接