在Windows PowerShell上重定向打印的unicode字符时编码错误



使用python 3,运行以下代码

print("some box drawing:")
print("┌─┬┼┴┐")

通过

py my_app.py

打印

some box drawing:
┌─┬┼┴┐

如你所料。

但是,如果你用 重定向这个(Windows或Linux)
py my_app.py > redirected.txt

你会得到以下异常:

UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-5: character maps to <undefined>
正如许多其他帖子所建议的那样,这个异常可以"修复"。通过在打印前调用sys.stdout.reconfigure(encoding='utf-8')。在linux和windows的cmd中,就是这样,问题解决了。然而,在Windows上使用PowerShell,输出看起来像这样:
some box drawing:
ΓöîΓöÇΓö¼Γö╝Γö┤ΓöÉ

这特别奇怪,因为它使用cmd.exe控制台工作得很好。

代码库作为可执行文件交付给客户,我不想要求他们在控制台中执行某些内容,以便我的程序可靠地工作。是否有一个程序化的方式有框绘制字符写正确时,重定向输出到一个文件使用windows PowerShell?

从这个答案中,我了解到,在PowerShell中重定向到utf-8根本不起作用,但utf-16可以。在启动时执行以下代码对我来说是有效的,有/没有重定向和在许多不同的控制台:

import os
import sys
is_redirected = not sys.stdout.isatty()
if is_redirected:
is_power_shell = len(os.getenv('PSModulePath', '').split(os.pathsep)) >= 3
if is_power_shell:
sys.stdout.reconfigure(encoding='utf-16')
else:
sys.stdout.reconfigure(encoding='utf-8')

我决定只在运行重定向时设置编码,并且在PowerShell中只设置utf-16,因为我想避免在其他设置中遇到其他不可预见的编码问题:检测power shell的代码片段取自这个答案,而检测来自这个答案的重定向的代码片段取自这个答案。

我自己觉得这个解决方案有点乱。如果你能找到更好的解决办法,我很乐意接受。

当直接在Windows中运行Python时,它内部使用Unicode api写入cmd窗口,并且不关心控制台编码设置为什么,但当重定向到一个文件时,它不知道。这就是为什么你可以用sys.stdout.reconfigure来告诉它。

Python也有一个环境变量PYTHONIOENCODING,它可以告诉它要使用的编码。chcp是shell命令,它将告诉您终端期望什么。

的例子:

C:tmp>chcp
Active code page: 437                     # Legacy U.S. DOS encoding
C:tmp>py -c "print('┌─┬┼┴┐')"            # this uses Unicode APIs
┌─┬┼┴┐
C:tmp>py -c "print('┌─┬┼┴┐')" >x         # this uses an OS-specific default encoding
Traceback (most recent call last):        # Windows-1252 on U.S. Windows.
File "<string>", line 1, in <module>
File "D:devPython311Libencodingscp1252.py", line 19, in encode
return codecs.charmap_encode(input,self.errors,encoding_table)[0]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-5: character maps to <undefined>
C:tmp>set PYTHONIOENCODING=cp437         # code page 437 supports box drawing characters
C:tmp>py -c "print('┌─┬┼┴┐')" >x         # file is written encoded in cp437
C:tmp>type x                             # matches terminal encoding and displays correctly
┌─┬┼┴┐
C:tmp>chcp 65001                         # UTF-8 code page
Active code page: 65001
C:tmp>type x                             # cp437 doesn't decode properly
������
C:tmp>set PYTHONIOENCODING=utf8          # Use UTF8
C:tmp>py -c "print('┌─┬┼┴┐')" >x         # write file encoded in UTF8
C:tmp>type x                             # matches terminal code page now
┌─┬┼┴┐

最新更新