代码之家  ›  专栏  ›  技术社区  ›  Tom Whittock

python os.walk和日语文件名崩溃[重复]

  •  2
  • Tom Whittock  · 技术社区  · 14 年前

    可能重复:
    Python, Unicode, and the Windows console

    我有一个文件名为“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的,所以我对我要用它做什么感到困惑。有人有什么想法吗?

    2 回复  |  直到 14 年前
        1
  •  7
  •   Glenn Maynard    14 年前

    到目前为止,似乎所有的答案都来自于那些认为Windows控制台类似于Unix终端的Unix用户,而事实并非如此。

    问题是,不能使用普通的底层文件I/O函数将Unicode输出写入Windows控制台。Windows API WriteConsole 需要使用。python应该透明地完成这项工作,但事实并非如此。

    如果将输出重定向到一个文件,则会有另一个问题:Windows文本文件历史上都在ANSI代码页中,而不是Unicode。现在在Windows中,您可以相当安全地将UTF-8写入文本文件,但默认情况下,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。

    在任何一种情况下,都是相反的。不能用write console输出到常规文件(它实际上不是一个字节API,而是一个utf-16;pywin隐藏了这个细节),也不能将utf-8写入Windows控制台。

    而且,它确实应该使用get-osfhandle将句柄获取到stdout和stderr,而不是假设它们被分配到标准句柄,但该API似乎没有任何pywin绑定。

        2
  •  -2
  •   André Caron    14 年前

    对于硬编码字符串,需要 specify the encoding at the top of source files . 对于来自其他源的字节字符串输入-例如 os.walk -,您需要指定字节字符串的编码(参见unutbu的答案)。