2016-09-28 6 views
3

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

Я провел дни, пытаясь решить эту проблему безрезультатно. Но сегодня я нашел поток на python website, другой на 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, и я сделал это без каких-либо проблем). Это верно?

+0

У меня есть несколько версий Python. Я не мог воспроизвести на Windows 10 64-битный, с Python 3.3.5 64-битным или Python 3.5.2 64-разрядным, но мог бы с Python 2.7.12 32-бит. Он выходит, как описано, но вы сказали, что используете Python 3. Возможно, это 32-битная проблема с 64-разрядной версией? Вы используете консоль cmd.exe Windows или что-то еще? –

+0

@MarkTolonen, это воспроизводится во всех версиях Windows при использовании консоли (conhost; cmd - это просто оболочка), которая не была предназначена для кодовой страницы 65001. Ввод одного символа, отличного от ASCII, вызывает пустое чтение, которое REPL Python и 'input' обрабатывается как EOF. Проблема заключается в том, что conhost.exe предполагает, что он кодирует свой входной буфер UTF-16 кодовой странице ANSI с 1 байтом на символ, поэтому для не-ASCII UTF-8 буфер кодировки 'WideCharToMultiByte' слишком мал. Чтение не выполняется, но оно возвращается клиенту как «успешное» чтение 0 байт, т. Е. Конец файла. – eryksun

+0

@eryksun, Да, я знаю, что он сломан, но все же, на 64-битном Python на 64-битной Windows 10, я могу напечатать на cmd.exe 'print ('ä')' с международным IME и распечатать правильно. Поэтому версия «воспроизводимая во всех Windows» является неточной, по крайней мере для этого конкретного примера. –

ответ

5

Использование Unicode в консоли Windows для Python 2.7 и 3.x (до 3.6), установите и активируйте win_unicode_console. Это использует широкоформатные функции ReadConsoleW и WriteConsoleW, как и другие консольные программы, поддерживающие Unicode, такие как cmd.exe и powershell.exe. Для Python 3.6 был добавлен новый класс ввода-вывода io._WindowsConsoleIO. Он читает и записывает кодированный текст 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, чтобы обойти эту ошибку.