Кодовая страница chcp 65001 приводит к завершению программы без ошибок

проблема
Проблема возникает, когда я хочу ввести символ Unicode в интерпретаторе Python (для простоты я использовал a-umlaut в примере, но я впервые столкнулся с этим для символов фарси). Всякий раз, когда я использую Python с chcp 65001 кодовой страницы, а затем попробуйте ввести хотя бы один символ Unicode, Python завершается без ошибок.

Я потратил несколько дней, пытаясь решить эту проблему безрезультатно. Но сегодня я нашел на веб-сайте python одну ветку, другую на MySQL и другую на Lua-пользователей, вопросы о которой возникли в связи с этим внезапным выходом, хотя без какого-либо решения, а некоторые говорили, что chcp 65001 по своей сути нарушен.

Было бы хорошо узнать раз и навсегда, связана ли эта проблема с chcp-дизайном или существует ли возможный обходной путь.

Воспроизвести ошибку

chcp 65001

Python 3.X:

Оболочка Python

print('ä')

результат: он просто выходит из оболочки

Тем не менее, это работает python.exe -c "print('ä')" а также это: print('\u00e4')

результат: ä

в Luajit2.0.4

print('ä')

результат: он просто выходит из оболочки

Однако это работает: print('\xc3\xa4')

Я пришел к этому наблюдению до сих пор:

  1. прямой вывод с командной строкой работает.
  2. Основанный на Unicode, основанный на шестнадцатеричном эквиваленте символьный работает.

Так что это не ошибка Python, и мы не можем использовать символ Unicode непосредственно в программах CLI в командной строке Windows или в любом из его оболочек, таких как Conemu, Cmder (я использую Cmder, чтобы иметь возможность видеть и использовать символы Unicode в Windows оболочка и я сделал это без проблем). Это правильно?

1 ответ

Решение

Чтобы использовать Unicode в консоли Windows для Python 2.7 и 3.x (до 3.6), установите и включите win_unicode_console. Это использует функции широких символов ReadConsoleW а также WriteConsoleW так же, как и другие консольные программы, поддерживающие Unicode, такие как cmd.exe и powershell.exe. Для Python 3.6 новый io._WindowsConsoleIO Класс raw I/O был добавлен. Он читает и записывает текст в кодировке UTF-8 (для кросс-платформенной совместимости с Unix - программами "получить байт"), но внутри он использует широкоформатный API путем транскодирования в и из UTF-16LE.

Проблема, с которой вы сталкиваетесь при вводе данных, отличных от ASCII, воспроизводится в консоли для всех версий Windows, включая Windows 10. Процесс хоста консоли, т.е. conhost.exe, не был разработан для UTF-8 (кодовая страница 65001) и не был обновлен, чтобы поддерживать его последовательно. В частности, ввод без ASCII вызывает пустое чтение. Это, в свою очередь, приводит к выходу REPL из Python и встроенному input поднимать EOFError,

Проблема состоит в том, что conhost кодирует свой входной буфер UTF-16, предполагая однобайтовую кодовую страницу, такую ​​как кодовые страницы OEM и ANSI в западных локалях (например, 437, 850, 1252). UTF-8 - это многобайтовое кодирование, в котором символы не ASCII кодируются в виде от 2 до 4 байтов. Для обработки UTF-8 необходимо было бы кодировать в несколько итераций M / 4 символы, где M - остальные байты, доступные из N-байтового буфера. Вместо этого он предполагает, что запрос на чтение N байтов является запросом на чтение N символов. Затем, если на входе есть один или несколько не-ASCII символов, внутренний WideCharToMultiByte вызов завершается неудачно из-за недостаточного размера буфера, и консоль возвращает "успешное" чтение 0 байтов.

Вы можете не наблюдать именно эту проблему в Python 3.5, если установлен модуль pyreadline. Python 3.5 автоматически пытается импортировать readline, В случае pyreadline ввод читается через функцию широких символов ReadConsoleInputW, Это низкоуровневая функция для чтения записей ввода с консоли. В принципе это должно работать, но на практике вход print('ä') читается REPL как print(''), Для не ASCII символа, ReadConsoleInputW возвращает последовательность Alt+Numpad KEY_EVENT записей. Последовательность представляет собой OEM-кодировку с потерями, которую можно игнорировать, за исключением последней записи, которая имеет входной символ в UnicodeChar поле. Очевидно pyreadline игнорирует всю последовательность.

До Windows 8 вывод с использованием кодовой страницы 65001 также прерывался. Он печатает текст мусора пропорционально количеству не-ASCII символов. В этом случае проблема в том, что WriteFile а также WriteConsoleA неправильно возвращает количество кодов UTF-16, записанных в экранный буфер, вместо количества байтов UTF-8. Это сбивает с толку буферизованного писателя Python, что приводит к повторным записям того, что он считает оставшимися неписанными байтами. Эта проблема была исправлена ​​в Windows 8 как часть переписывания API внутренней консоли для использования устройства ConDrv вместо порта LPC. Более старые версии Windows могут использовать ConEmu или ANSICON, чтобы обойти эту ошибку.

Другие вопросы по тегам