1

小さなCLIノートブックアプリケーションを作成する過程で、cmdPythonライブラリを使用することにしました(cmdPyMOTWも参照)。

私のシェルは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
4

2 に答える 2

2

SOにはこのような別の質問があると思いましたが、特にC共有ライブラリモジュールに対してです。この答えはそこでより適切だったかもしれませんが、今はリンクが見つかりません:)

簡単に言えば、私の答えは -locale.setlocale(locale.LC_ALL, '')モジュールをロードする前に試してください (私cmd自身はまだ使用していません)。さらに詳細に:

Subversion (SVN) 用の SWIG Python バインディングを使用しようとしていました。これらは基本的に、SVN C ライブラリ コードから直接、SWIG によって作成された Python 用の自動インターフェイスです ( libsvn1)。ターミナルから実行すると、コードsvn status MyWorkingCopyにフックされlibsvnます-そして、何年も失敗していません(そのリポジトリの場合)。しかし、同じ端末から Python の例 (と同じことを行うsvn status) を実行すると (同じコードにフックされます) libsvn、libsvn/SWIG から UTF-8 関連のエラーが発生し、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')

そこで、ターミナルから実行した場合と Python から実行した場合 (同じターミナル内) で、C コードが何を認識するかを確認します。さらにデバッグすると、実際にはSVNがメモリ割り当てに使用するlibsvn別のライブラリ(Apache Portable Runtime)からのものであることが判明しました。libapr私がやったことは、スタンドアロンのCプログラムlibsvnで使用する文字列のコピーの繰り返しを書くことです。libapr次に、SWIG を介して Python モジュールとしてビルドしました。このプログラムaprtestは、文字列を引数として受け取り、libaprエンジンを呼び出してそれをコピーし、結果を表示します。そのソースはここに投稿されています:

私が使用したライブラリのバージョン (Ubuntu 11.04)については、スクリプトbuild-aprtest.shを参照してください。ビルドするには、実行します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報告にもかかわらず、エンジンはコマンドラインからの UTF-8 入力で明らかに失敗しましたUTF-8aprtest_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 of apr_os_locale_encoding on Windows :

... この 55 の異なる現在のロケールの 1 つを選択することは、おそらく APR ではなく、アプリケーションによってのみ適切に行うことができます。

したがって、C アプリケーションでコーディングsetlocale()することにより、明らかにデフォルトのロケールを明示的に選択するので、それをlibapr認識しています。テスト ケースでは、この への呼び出しは へsetlocaleの呼び出しの前に発生する必要がありapr_xlate_openます。

さて、投稿されたバージョンのaprtestは を実行しないので、Python バージョンを使用すると、Pythonsetlocaleから何が起こっているかを確認できます (これ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東京
>>> 

したがって、C アプリケーションが Python で見ているものを確認するには、locale.getlocale()( NOTlocale.getdefaultlocale() ) を使用します。私が今理解している方法では、デフォルトと見なされるが、アプリケーションの起動時に必ずデフォルトとして適用さgetdefaultlocaleれる、どこかに保存されたいくつかの OS/ユーザー設定を返します。現在適用されている実際のロケール設定を取得します。そして、空の文字列で呼び出すと、コードの残りの部分が次のようになると思います: デフォルト設定 ( で指定されたもの) を読み取り、デフォルト設定を現在の設定として適用します。getlocalesetlocalegetdefaultlocale

最後に、関連しているように見えますが、stdin/のエンコーディング設定はstdout(明らかに) 現在のロケールのエンコーディングとは何の関係もありません (少なくとも、その環境で実行されている C プログラムから見れば)。

これが誰かに役立つことを願っています、
乾杯!

于 2013-04-12T17:17:43.843 に答える
1

あなたのコードは私にとって完璧に機能します、カール。これを参照してください:

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

また、notes.mdファイルには適切なエントリが含まれています。したがって、ここで問題があるとは思いませんcmdが、おそらく端末の設定に何か問題があると思います。追加してみる

export LESSCHARSET=utf-8

あなたの.profile

于 2013-01-15T09:51:02.797 に答える