Кодовая страница 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')
Я пришел к этому наблюдению до сих пор:
- прямой вывод с командной строкой работает.
- Основанный на 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, чтобы обойти эту ошибку.