web-dev-qa-db-ja.com

WindowsのPython 2.xのコマンドライン引数からUnicode文字を読み取る

PythonスクリプトでWindowsのUnicodeコマンドライン引数を読み取れるようにしたいのですが、sys.argvはUnicodeではなくローカルエンコーディングでエンコードされた文字列のようです。どうすれば読み取ることができますか?完全なUnicodeのコマンドライン?

コード例:argv.py

import sys

first_arg = sys.argv[1]
print first_arg
print type(first_arg)
print first_arg.encode("hex")
print open(first_arg)

日本語のコードページ用に設定されたPCで、次のようになります。

C:\temp>argv.py "PC・ソフト申請書08.09.24.doc"
PC・ソフト申請書08.09.24.doc
<type 'str'>
50438145835c83748367905c90bf8f9130382e30392e32342e646f63
<open file 'PC・ソフト申請書08.09.24.doc', mode 'r' at 0x00917D90>

それはShift-JISでエンコードされていると私は信じており、そのファイル名で「機能」します。ただし、Shift-JIS文字セットに含まれていない文字を含むファイル名では壊れます。最後の「open」呼び出しは失敗します。

C:\temp>argv.py Jörgen.txt
Jorgen.txt
<type 'str'>
4a6f7267656e2e747874
Traceback (most recent call last):
  File "C:\temp\argv.py", line 7,
in <module>
    print open(first_arg)
IOError: [Errno 2] No such file or directory: 'Jorgen.txt'

注-私が話しているのはPython 2.xであり、Python 3.0ではありません。Python 3.0 sys.argvを適切なUnicodeとして提供しますが、Python 3.0(サードパーティライブラリのサポートがないため)に移行するのはまだ少し早いです。

更新:

いくつかの回答では、sys.argvがエンコードされているものに応じてデコードする必要があると述べています。問題は、完全なUnicodeではないため、一部の文字を表現できないことです。

これが私に悲しみを与えるユースケースです:私は Windowsエクスプローラーで.pyファイルへのファイルのドラッグアンドドロップを有効にしました を持っています。システムのデフォルトのコードページにないものも含め、あらゆる種類の文字を含むファイル名があります。私のPythonスクリプトは、文字が現在のコードページエンコーディングで表現できない場合、すべての場合にsys.argvを介して渡された正しいUnicodeファイル名を取得しません。

確かに、完全なUnicodeでコマンドラインを読み取るためのWindows APIがいくつかあります(そしてPython 3.0はそれを行います)。Python 2.xインタープリターはそうではないと思いますそれを使用します。

29
Craig McQueen

これが私が探している解決策であり、Windows GetCommandLineArgvW関数を呼び出します。
WindowsでUnicode文字を使用してsys.argvを取得 (ActiveStateから)

しかし、その使用法を簡素化し、特定の使用法をより適切に処理するために、いくつかの変更を加えました。これが私が使用するものです:

win32_unicode_argv.py

"""
win32_unicode_argv.py

Importing this will replace sys.argv with a full Unicode form.
Windows only.

From this site, with adaptations:
      http://code.activestate.com/recipes/572200/

Usage: simply import this module into a script. sys.argv is changed to
be a list of Unicode strings.
"""


import sys

def win32_unicode_argv():
    """Uses Shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
    strings.

    Versions 2.x of Python don't support Unicode in sys.argv on
    Windows, with the underlying Windows API instead replacing multi-byte
    characters with '?'.
    """

    from ctypes import POINTER, byref, cdll, c_int, windll
    from ctypes.wintypes import LPCWSTR, LPWSTR

    GetCommandLineW = cdll.kernel32.GetCommandLineW
    GetCommandLineW.argtypes = []
    GetCommandLineW.restype = LPCWSTR

    CommandLineToArgvW = windll.Shell32.CommandLineToArgvW
    CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
    CommandLineToArgvW.restype = POINTER(LPWSTR)

    cmd = GetCommandLineW()
    argc = c_int(0)
    argv = CommandLineToArgvW(cmd, byref(argc))
    if argc.value > 0:
        # Remove Python executable and commands if present
        start = argc.value - len(sys.argv)
        return [argv[i] for i in
                xrange(start, argc.value)]

sys.argv = win32_unicode_argv()

さて、私がそれを使用する方法は、単に行うことです:

import sys
import win32_unicode_argv

そしてそれ以降、sys.argvはUnicode文字列のリストです。 Python optparseモジュールはそれを解析して満足しているようです。これは素晴らしいことです。

29
Craig McQueen

エンコーディングの扱いは非常に混乱します。

I believe if your inputing data via the commandline it will encode the data as whatever your system encoding is and is not unicode. (Even copy/paste should do this)

したがって、システムエンコーディングを使用してUnicodeにデコードするのは正しいはずです。

_import sys

first_arg = sys.argv[1]
print first_arg
print type(first_arg)

first_arg_unicode = first_arg.decode(sys.getfilesystemencoding())
print first_arg_unicode
print type(first_arg_unicode)

f = codecs.open(first_arg_unicode, 'r', 'utf-8')
unicode_text = f.read()
print type(unicode_text)
print unicode_text.encode(sys.getfilesystemencoding())
_

次のWillを実行すると、次のように出力されます。プロンプト> python myargv.py "PC・ソフトウェアソフトウェア08.09.24.txt"

_PC・ソフト申請書08.09.24.txt
<type 'str'>
<type 'unicode'>
PC・ソフト申請書08.09.24.txt
<type 'unicode'>
?日本語
_

「PC・ソフトウェアソフトウェア08.09.24.txt」に「日本語」というテキストが含まれている場合。 (Windowsのメモ帳を使用してファイルをutf8としてエンコードしましたが、印刷時に最初に「?」が表示される理由について少し困惑しています。メモ帳がutf8を保存する方法と関係がありますか?)

文字列の「decode」メソッドまたは組み込みのunicode()を使用して、エンコーディングをUnicodeに変換できます。

_unicode_str = utf8_str.decode('utf8')
unicode_str = unicode(utf8_str, 'utf8')
_

また、エンコードされたファイルを扱う場合は、組み込みのopen()の代わりにcodecs.open()関数を使用することをお勧めします。これにより、ファイルのエンコーディングを定義し、指定されたエンコーディングを使用して、コンテンツを透過的にUnicodeにデコードできます。

したがって、content = codecs.open("myfile.txt", "r", "utf8").read()contentを呼び出すとユニコードになります。

codecs.open: http://docs.python.org/library/codecs.html?#codecs.open

何かがわからない場合はお知らせください。

まだ読んでいない場合は、Unicodeとエンコーディングに関するJoelの記事を読むことをお勧めします: http://www.joelonsoftware.com/articles/Unicode.html

12
monkut

これを試して:

import sys
print repr(sys.argv[1].decode('UTF-8'))

たぶん、CP437CP1252またはUTF-8に置き換える必要があります。レジストリキーHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage\OEMCPから適切なエンコーディング名を推測できるはずです。

2
pts

コマンドラインはWindowsエンコーディングである可能性があります。引数をunicodeオブジェクトにデコードしてみてください。

args = [unicode(x, "iso-8859-9") for x in sys.argv]
0
a paid nerd