Чтение символов Юникода из аргументов командной строки в Python 2.x в Windows - PullRequest
29 голосов
/ 11 мая 2009

Я хочу, чтобы мой скрипт на Python мог читать аргументы командной строки Unicode в Windows. Но похоже, что 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)

На моем компьютере, настроенном для японской кодовой страницы, я получаю:

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, поэтому некоторые символы не представимы.

Вот пример использования, который меня огорчает: у меня включено перетаскивание файлов в .py файлы в проводнике Windows . У меня есть имена файлов со всевозможными символами, включая те, которые отсутствуют в системной кодовой странице по умолчанию. Мой сценарий Python не получает правильные имена файлов Unicode, переданные ему через sys.argv во всех случаях, когда символы не могут быть представлены в текущей кодировке кодовой страницы.

Конечно, есть некоторый Windows API для чтения командной строки с полным Unicode (и Python 3.0 делает это). Я предполагаю, что интерпретатор Python 2.x не использует его.

Ответы [ 4 ]

29 голосов
/ 11 мая 2009

Вот решение, которое как раз то, что я ищу, для вызова функции Windows GetCommandLineArgvW:
Получить sys.argv с символами Unicode под Windows (из 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, кажется, рад разобрать его, и это здорово.

12 голосов
/ 11 мая 2009

Работа с кодировками очень запутанная.

Я полагаю , если вы вводите данные через командную строку, они будут кодировать эти данные в соответствии с кодировкой вашей системы, а не в кодировке 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())

работает следующее: Подсказка> python myargv.py "ПК ・ ソ フ ト 申請書 08.09.24.txt"

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

Где "PC ・ ソ フ ト 申請書 .09 08.09.24.txt" содержал текст "日本語". (Я закодировал файл как utf8 с помощью блокнота Windows, я немного озадачен тем, почему в начале печати стоит знак «?». Как-то связано с тем, как блокнот сохраняет utf8?)

Для преобразования кодировки в юникод можно использовать метод 'decode' для строк или встроенную функцию unicode ().

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

Также, если вы имеете дело с закодированными файлами, вы можете использовать функцию codecs.open () вместо встроенной функции open (). Он позволяет вам определить кодировку файла и затем использовать данную кодировку для прозрачного декодирования содержимого в Unicode.

Так что, когда вы звоните content = codecs.open("myfile.txt", "r", "utf8").read() content будет в Unicode.

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

Если я что-то не понимаю, пожалуйста, дайте мне знать.

Если вы еще этого не сделали, я рекомендую прочитать статью Джоэла о юникоде и кодировке: http://www.joelonsoftware.com/articles/Unicode.html

2 голосов
/ 11 мая 2009

Попробуйте это:

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

Возможно, вам придется заменить CP437 или CP1252 на UTF-8. Вы должны быть в состоянии определить правильное имя кодировки из ключа реестра HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage\OEMCP

0 голосов
/ 11 мая 2009

Командная строка может быть в кодировке Windows. Попробуйте расшифровать аргументы в unicode объекты:

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