Python os.walk和日本文件名崩溃

可能重复:
Python,Unicode和Windows控制台

我有一个文件夹“01 – 娜娜块.txt”

我在与文件相同的文件夹中的交互式提示符下打开python,然后尝试遍历该文件夹:

Python 3.1.2 (r312:79149, Mar 21 2010, 00:41:52) [MSC v.1500 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import os >>> for x in os.walk('.'): ... print(x) ... Traceback (most recent call last): File "<stdin>", line 2, in <module> File "C:\dev\Python31\lib\encodings\cp850.py", line 19, in encode return codecs.charmap_encode(input,self.errors,encoding_map)[0] UnicodeEncodeError: 'charmap' codec can't encode characters in position 17-21: character maps to <undefined> 

显然,我正在使用的编码不能处理日文字符。 精细。 但是,据我所知,Python 3.1本来就是unicode,所以我不知道该怎么做。 有人有主意吗?

看起来好像所有的答案都来自Unix人,他们认为Windows控制台就像一个Unix终端,事实并非如此。

问题是,您不能使用正常的基础文件I / O函数将Unicode输出写入Windows控制台。 需要使用Windows API WriteConsole 。 Python应该可以做到这一点透明,但事实并非如此。

如果将输出重定向到一个文件,还有一个问题:Windows文本文件历史上是在ANSI代码页中,而不是Unicode。 如今,你可以相当安全地将UTF-8编写成Windows中的文本文件,但Python默认情况下不会这样做。

我认为它应该做这些事情,但是这里有一些代码来实现它。 如果你不想要,你不必担心细节; 只需调用ConsoleFile.wrap_standard_handles()。 您需要安装PyWin才能访问必要的API。

 import os, sys, io, win32api, win32console, pywintypes def change_file_encoding(f, encoding): """ TextIOWrapper is missing a way to change the file encoding, so we have to do it by creating a new one. """ errors = f.errors line_buffering = f.line_buffering # f.newlines is not the same as the newline parameter to TextIOWrapper. # newlines = f.newlines buf = f.detach() # TextIOWrapper defaults newline to \r\n on Windows, even though the underlying # file object is already doing that for us. We need to explicitly say "\n" to # make sure we don't output \r\r\n; this is the same as the internal function # create_stdio. return io.TextIOWrapper(buf, encoding, errors, "\n", line_buffering) class ConsoleFile: class FileNotConsole(Exception): pass def __init__(self, handle): handle = win32api.GetStdHandle(handle) self.screen = win32console.PyConsoleScreenBufferType(handle) try: self.screen.GetConsoleMode() except pywintypes.error as e: raise ConsoleFile.FileNotConsole def write(self, s): self.screen.WriteConsole(s) def close(self): pass def flush(self): pass def isatty(self): return True @staticmethod def wrap_standard_handles(): sys.stdout.flush() try: # There seems to be no binding for _get_osfhandle. sys.stdout = ConsoleFile(win32api.STD_OUTPUT_HANDLE) except ConsoleFile.FileNotConsole: sys.stdout = change_file_encoding(sys.stdout, "utf-8") sys.stderr.flush() try: sys.stderr = ConsoleFile(win32api.STD_ERROR_HANDLE) except ConsoleFile.FileNotConsole: sys.stderr = change_file_encoding(sys.stderr, "utf-8") ConsoleFile.wrap_standard_handles() print("English 漢字 Кири́ллица") 

这有点棘手:如果stdout或stderr是控制台,我们需要使用WriteConsole输出; 但是如果它不是(例如foo.py>文件),这不会起作用,我们需要改变文件的编码为UTF-8。

在任何一种情况下,相反都行不通。 您不能使用WriteConsole输出到常规文件(它实际上不是一个字节API,而是一个UTF-16文件; PyWin隐藏了这个细节),而且不能将UTF-8写入Windows控制台。

另外,它确实应该使用_get_osfhandle获取stdout和stderr的句柄,而不是假定它们被分配到标准句柄,但是该API似乎没有任何PyWin绑定。

对于硬编码的字符串,您需要在源文件的顶部指定编码 。 对于从其他来源输入的字节串(例如os.walk ),您需要指定字节串的编码(请参阅unutbu的答案)。