Использование ввода utf-8 для модуля cmd Python

В процессе создания небольшого приложения для ноутбуков CLI я решил пойти с библиотекой python cmd (см. Также cmd на PyMOTW).

Моя оболочка UTF-8.

 → echo $LANG fr_FR.utf-8 → echo $LC_ALL fr_FR.utf-8 

И он работает достаточно хорошо.

 → echo "東京"東京 

Запуск кода моего маленького приложения и попытки использования utf-8:

 → python nb.py log> foobar 2013-01-15 foobar log> æ±äº¬ 2013-01-15 æ±äº¬ 

Отредактировано Ожидаемый ввод / вывод. Когда я печатаю символы utf-8, будь то акцент или японские символы, я получаю мусор.

 log> 東京2013-01-15 東京 

Поэтому при запуске программы командная строка меняет тип ввода.

 #!/usr/bin/env python2.7 # encoding: utf-8 import datetime import os.path import logging import cmd ROOT = "~/test/" NOTENAME = "notes.md" def todaynotepath(rootpath, notename): isodate = datetime.date.today().isoformat() isodate.replace("-", "/") return rootpath + isodate.replace("-", "/") + "/%s" % (notename) def addcontent(content): logging.info(content) class NoteBook(cmd.Cmd): """Simple cli notebook.""" prompt = "log> " def precmd(self, line): # What is the date path NOW notepath = todaynotepath(ROOT, NOTENAME) # if the directory of the note doesn't exist, create it. notedir = os.path.dirname(notepath) if not os.path.exists(notedir): os.makedirs(notedir) # if the file for notes today doesn't exist, create it. logging.basicConfig(filename=notepath, level=logging.INFO, format='%(asctime)s - %(message)s') return cmd.Cmd.precmd(self, line) def default(self, line): if line: print datetime.date.today().isoformat(), line addcontent(line) def do_EOF(self, line): return True def postloop(self): print if __name__ == "__main__": NoteBook().cmdloop() 

Поэтому я предполагаю, что в исходном классе cmd могут быть переопределены вещи. Я проверил модуль, но пока не повезло.

Редактировать 2: Добавлен LESSCHARSET как рекомендовано @dda

 LANG=fr_FR.utf-8 LANGUAGE=fr_FR.utf-8 LC_ALL=fr_FR.utf-8 LC_CTYPE=fr_FR.UTF-8 LESSCHARSET=utf-8 

2 Solutions collect form web for “Использование ввода utf-8 для модуля cmd Python”

Ваш код отлично работает для меня, Карл. Видеть это:

 dda$ ./nb.py log> tagada 2013-01-15 tagada log> 香港2013-01-15 香港log> 

Файл notes.md содержит правильные записи. Поэтому я не думаю, что это ошибка, это ошибка, но, вероятно, что-то в ваших настройках терминала. Попробуйте добавить

 export LESSCHARSET=utf-8 

в вашем .profile .

Я думал, что существует еще один такой вопрос, как SO, но специально для C-библиотечного модуля; этот ответ, возможно, был более уместным, но я не могу найти ссылку сейчас :)

locale.setlocale(locale.LC_ALL, '') говоря, я бы ответил – попробуйте locale.setlocale(locale.LC_ALL, '') перед загрузкой модуля (я еще не использовал cmd ). Более детально:

Я пытался использовать привязки SWIG Python для Subversion (SVN). Это в основном автоматический интерфейс для Python, созданный SWIG, непосредственно из библиотечного кода SVN C ( libsvn1 ). Когда я запускаю svn status MyWorkingCopy с терминала, он перехватывает код libsvn – и он не сработал уже много лет (для этого репозитория). Но когда я запускал пример Python (делающий то же самое, что и svn status ), который перехватывает один и тот же код libsvn из того же терминала, тогда я получил бы ошибку UTF-8, связанную с libsvn / SWIG, которая могла бы сработать мой скрипт Python.

Это означает, что Python каким-то образом «повлиял» на библиотеку, чтобы вести себя иначе по отношению к наборам символов. Но мой терминал постоянно сообщает:

 $ locale LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_CTYPE="en_US.UTF-8" ... LC_IDENTIFICATION="en_US.UTF-8" LC_ALL= 

Таким образом, речь идет не о том, что думает терминал / оболочка ( bash в этом случае) – о том, что основной C-код (из libsvn в этом случае) думает о текущих настройках. И то же, я думал, относится к python:

 $ python -c 'import locale; print locale.getdefaultlocale()' ('en_US', 'UTF-8') 

Итак, теперь речь идет о том, что видит C-код, когда он запускался с терминала или при запуске с Python (в том же терминале). Отладка libsvn дальше, оказалось, что это действительно происходит из другой библиотеки libapr (Apache Portable Runtime), которую SVN использует для выделения памяти. То, что я закончил, пишет повторение строкового копирования, выполненного libsvn который использует libapr в автономной программе на C; а затем построил его через SWIG как модуль Python. Эта программа, aprtest , принимает строку как аргумент, вызывает механизм libapr для ее копирования и отображает результат; источник для него размещен здесь:

См. Скрипт build-aprtest.sh для версий библиотек, с которыми я работал (Ubuntu 11.04); для сборки, запустите bash build-aprtest.sh .

Теперь, если вы запустите исполняемый файл, созданный таким образом в терминале, вы получите:

 $ locale LANG=en_US.UTF-8 ... $ ./aprtest "test" LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test $ ./aprtest "test東京" LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 22 

libapr механизм libapr не смог выполнить вход UTF-8 из командной строки, несмотря на сообщение терминала UTF-8 . И когда мы запускаем как общий модуль (называемый aprtest_s ) через Python:

 $ python -c 'import aprtest_s; aprtest_s.pysmain("test")' LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test $ python -c 'import aprtest_s; aprtest_s.pysmain("test東京")' LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 22 

… то же самое происходит ( кстати, для той же проблемы с SVN и APR, но для Perl, см. Есть ли переменная или функция, которая возвращает кодировку собственной платформы (APR_LOCALE_CHARSET) ). Таким образом, мы можем заключить:

  • Не имеет значения, запускается ли программа C непосредственно с терминала или через Python – программа C просто видит разные настройки языка / кодировки, из которых может отображаться вызывающая программа
  • Нет никаких проблем с строками ASCII, только для UTF-8

Итак, как же тогда клиент svn корректно работает с терминалом, а в конечном счете использует libapr без сбоев? Ну, как это видно из комментариев источника для aprtest_s.c ; это, установив собственный язык программы, используя функцию C setlocale(LC_CTYPE,"") , которая, как оказалось, устанавливает все категории локали процесса . Эта проблема действительно упоминается в списке рассылки apr-dev: Re: Misbehavior apr_os_locale_encoding в Windows :

… этот выбор одного из 55 разных текущих локалей, вероятно, может быть выполнен только приложением, а не APR.

Итак, setlocale() в приложении C, мы явно выбираем локаль по умолчанию явно, поэтому libapr знает об этом. В тестовом случае этот вызов setlocale должен произойти до вызова apr_xlate_open .

Теперь опубликованная версия aprtest не делает setlocale , поэтому мы можем видеть, что происходит с Python (обратите внимание также на это ), когда мы используем версию Python, locale.setlocale() :

 $ PYTHONIOENCODING='utf-8' echo 'import sys;print sys.stdin.encoding' | python None $ echo 'import sys;print sys.stdin.encoding' | PYTHONIOENCODING='utf-8' python utf-8 $ locale LANG=en_US.UTF-8 LANGUAGE=en_US:en ... $ python Python 2.7.1+ (r271:86832, Sep 27 2012, 21:16:52) [GCC 4.5.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import aprtest_s >>> aprtest_s.print_locale() LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 >>> aprtest_s.pysmain("test") LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test >>> aprtest_s.pysmain("test東京") LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 22 >>> import locale >>> print locale.getdefaultlocale() ('en_US', 'UTF-8') >>> print locale.getlocale() (None, None) >>> import sys >>> print sys.stdin.encoding UTF-8 >>> locale.setlocale(locale.LC_ALL, '') 'en_US.UTF-8' >>> print sys.stdin.encoding UTF-8 >>> print locale.getlocale() ('en_US', 'UTF-8') >>> aprtest_s.pysmain("test") LC_CTYPE 0 CODESET 14 UTF-8 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test >>> aprtest_s.pysmain("test東京") LC_CTYPE 0 CODESET 14 UTF-8 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test東京>>> , $ PYTHONIOENCODING='utf-8' echo 'import sys;print sys.stdin.encoding' | python None $ echo 'import sys;print sys.stdin.encoding' | PYTHONIOENCODING='utf-8' python utf-8 $ locale LANG=en_US.UTF-8 LANGUAGE=en_US:en ... $ python Python 2.7.1+ (r271:86832, Sep 27 2012, 21:16:52) [GCC 4.5.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import aprtest_s >>> aprtest_s.print_locale() LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 >>> aprtest_s.pysmain("test") LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test >>> aprtest_s.pysmain("test東京") LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 22 >>> import locale >>> print locale.getdefaultlocale() ('en_US', 'UTF-8') >>> print locale.getlocale() (None, None) >>> import sys >>> print sys.stdin.encoding UTF-8 >>> locale.setlocale(locale.LC_ALL, '') 'en_US.UTF-8' >>> print sys.stdin.encoding UTF-8 >>> print locale.getlocale() ('en_US', 'UTF-8') >>> aprtest_s.pysmain("test") LC_CTYPE 0 CODESET 14 UTF-8 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test >>> aprtest_s.pysmain("test東京") LC_CTYPE 0 CODESET 14 UTF-8 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test東京>>> , $ PYTHONIOENCODING='utf-8' echo 'import sys;print sys.stdin.encoding' | python None $ echo 'import sys;print sys.stdin.encoding' | PYTHONIOENCODING='utf-8' python utf-8 $ locale LANG=en_US.UTF-8 LANGUAGE=en_US:en ... $ python Python 2.7.1+ (r271:86832, Sep 27 2012, 21:16:52) [GCC 4.5.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import aprtest_s >>> aprtest_s.print_locale() LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 >>> aprtest_s.pysmain("test") LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test >>> aprtest_s.pysmain("test東京") LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 22 >>> import locale >>> print locale.getdefaultlocale() ('en_US', 'UTF-8') >>> print locale.getlocale() (None, None) >>> import sys >>> print sys.stdin.encoding UTF-8 >>> locale.setlocale(locale.LC_ALL, '') 'en_US.UTF-8' >>> print sys.stdin.encoding UTF-8 >>> print locale.getlocale() ('en_US', 'UTF-8') >>> aprtest_s.pysmain("test") LC_CTYPE 0 CODESET 14 UTF-8 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test >>> aprtest_s.pysmain("test東京") LC_CTYPE 0 CODESET 14 UTF-8 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test東京>>> , $ PYTHONIOENCODING='utf-8' echo 'import sys;print sys.stdin.encoding' | python None $ echo 'import sys;print sys.stdin.encoding' | PYTHONIOENCODING='utf-8' python utf-8 $ locale LANG=en_US.UTF-8 LANGUAGE=en_US:en ... $ python Python 2.7.1+ (r271:86832, Sep 27 2012, 21:16:52) [GCC 4.5.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import aprtest_s >>> aprtest_s.print_locale() LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 >>> aprtest_s.pysmain("test") LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test >>> aprtest_s.pysmain("test東京") LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 22 >>> import locale >>> print locale.getdefaultlocale() ('en_US', 'UTF-8') >>> print locale.getlocale() (None, None) >>> import sys >>> print sys.stdin.encoding UTF-8 >>> locale.setlocale(locale.LC_ALL, '') 'en_US.UTF-8' >>> print sys.stdin.encoding UTF-8 >>> print locale.getlocale() ('en_US', 'UTF-8') >>> aprtest_s.pysmain("test") LC_CTYPE 0 CODESET 14 UTF-8 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test >>> aprtest_s.pysmain("test東京") LC_CTYPE 0 CODESET 14 UTF-8 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test東京>>> , $ PYTHONIOENCODING='utf-8' echo 'import sys;print sys.stdin.encoding' | python None $ echo 'import sys;print sys.stdin.encoding' | PYTHONIOENCODING='utf-8' python utf-8 $ locale LANG=en_US.UTF-8 LANGUAGE=en_US:en ... $ python Python 2.7.1+ (r271:86832, Sep 27 2012, 21:16:52) [GCC 4.5.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import aprtest_s >>> aprtest_s.print_locale() LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 >>> aprtest_s.pysmain("test") LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test >>> aprtest_s.pysmain("test東京") LC_CTYPE 0 CODESET 14 ANSI_X3.4-1968 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 22 >>> import locale >>> print locale.getdefaultlocale() ('en_US', 'UTF-8') >>> print locale.getlocale() (None, None) >>> import sys >>> print sys.stdin.encoding UTF-8 >>> locale.setlocale(locale.LC_ALL, '') 'en_US.UTF-8' >>> print sys.stdin.encoding UTF-8 >>> print locale.getlocale() ('en_US', 'UTF-8') >>> aprtest_s.pysmain("test") LC_CTYPE 0 CODESET 14 UTF-8 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test >>> aprtest_s.pysmain("test東京") LC_CTYPE 0 CODESET 14 UTF-8 apr_xlate_open: apr_err=0 apr_xlate_conv_buffer apr_err == 0 (*dest)->data: test東京>>> 

Таким образом, чтобы убедиться, что это такое, что приложение C видит в Python – используйте locale.getlocale() ( NOT locale.getdefaultlocale() ). Теперь, когда я понимаю это, getdefaultlocale возвращает некоторые OS / пользовательские настройки, сохраненные где-то, которые считаются по умолчанию, но обязательно применяются по умолчанию при запуске приложения; и getlocale получает фактические, в настоящее время применяемые настройки локали. И я думаю, когда мы вызываем setlocale с пустой строкой, это заставляет остальную часть кода: читать настройки по умолчанию (данные, заданные методом getdefaultlocale ), а затем применять настройки по умолчанию как текущие.

И в качестве окончательного примечания – хотя это выглядит связанным, настройки кодирования stdin / stdout (по-видимому) не имеют ничего общего с кодировкой текущей локали (по крайней мере, как видно из программы C, работающей в этой среде).

Надеюсь, это помогает кому-то,
Ура!

Python - лучший язык программирования в мире.