2010-09-24 5 views
2

Можно создать дубликат:
Python, Unicode, and the Windows consolePython os.walk и японский крах файла

У меня есть папка с именем "01 - ナ ナ ナ ン 塊 .txt"

открываю python в интерактивной подсказке в той же папке, что и файл, и попытайтесь пройти папку hierachy:

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 полностью вниз, как я понимаю, поэтому я не понимаю, что я должен делать с этим. У кого-нибудь есть идеи?

+1

См. Http://stackoverflow.com/questions/5419/python-unicode-and-the-windows-console - и, в конечном счете, см .: http://wiki.python.org/moin/PrintFails - я думаю, что это что вы ищете. – Thanatos

+0

Thanatos правильный - это ошибка печати. Мне грустно. Я думал, что Python прост в использовании :( –

+0

Оказывается, проблема в том, что это не связано с файлами - поддержка Unicode в Python 3 на Windows немного неоднородна - печать не работает в консоли, и файлы открываются в не-utf режиме (это был другой метод, который я пробовал перед отправкой здесь), поэтому у меня, казалось бы, не было вариантов сбрасывать то, что я просматривал. В дополнение к принятому ответу я также мог перепрыгнуть через кодеки.открыть обруч, чтобы создать файл, который представляет тип текста по умолчанию в Python, и посмотрел на это. Как непитонический. –

ответ

7

Кажется, что все ответы пока что от пользователей Unix, которые полагают, что консоль Windows похожа на терминал Unix, которого нет.

Проблема заключается в том, что вы не можете выводить выходные данные Unicode на консоль Windows, используя обычные функции ввода-вывода файлов. Необходимо использовать API Windows 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 漢字 Кири́ллица") 

Это немного сложнее: если стандартный вывод или STDERR это консоль, необходимо вывести с WriteConsole; но если это не (например, файл foo.py>), это не сработает, и нам нужно вместо этого изменить кодировку файла на UTF-8.

Противоположность в любом случае не будет работать. Вы не можете выводить в обычный файл с помощью WriteConsole (это не байтовый API, а UTF-16, PyWin скрывает эту деталь), и вы не можете писать UTF-8 на консоль Windows.

Также нужно использовать _get_osfhandle для получения дескриптора stdout и stderr, вместо того, чтобы предполагать, что они назначены стандартным дескрипторам, но у этого API нет привязки к PyWin.

+0

+1 - вы, кажется, первым на самом деле поняли проблему. Я думаю, что проблема с 'WriteConsoleW' и' WriteFile' известна в сообществе Python, но на самом деле реализация различия кажется сложной или по крайней мере непопулярной. – Philipp

+0

Python разработан в основном людьми Unix, и тратить время на нечетные детали платформ других народов никогда не привлекательно, но это действительно важно. Основные части Python в Windows (например, 'print') должны * не * быть ограничены кодировкой ANS-95 (на самом деле, они относятся к DOS). –

+0

Ничего себе. Это то, что мне нужно сделать, чтобы отобразить строку юникода в стандартном окне команд в Windows. Если бы это было не так грустно, это было бы забавно. Большое спасибо за то, что вы выполняете всю эту тяжелую работу по правильному внедрению выходных потоков. –

-2

Для жестко закодированных строк вам необходимо будет specify the encoding at the top of source files. Для ввода байтов из другого источника - например, os.walk - вам нужно указать кодировку байтовой строки (см. Ответ unutbu).

+0

В Windows нет байтовых строк, только строки UTF-16. – Philipp

+0

@Philipp: Все ядра на базе Windows NT поддерживают только строки UTF-16. Вы все равно можете вызывать версию ANSI для всех Win32 API, таких как 'FindFirstFileA()', чтобы получить список фидов, содержащий то, что Python вызывает bytestrings. Я предполагаю, что это то, что делает Python, потому что на моей машине Windows 'os.walk()' с Python 2.6.5 возвращает элементы класса 'str', которые являются байтовыми строками. –

+0

Я использую Python 3, который полностью utf-8.http: //www.python.org/dev/peps/pep-3120/ –

Смежные вопросы