Чтение символов Юникода из аргументов командной строки в Python 2.x в Windows
Я хочу, чтобы мой Python script мог читать аргументы командной строки 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 - окончательный "открытый" вызов завершается с ошибкой:
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 (из-за отсутствия поддержки сторонней библиотеки).
Update:
Несколько ответов сказали, что я должен декодировать в соответствии с тем, что закодировано в sys.argv
. Проблема заключается в том, что он не является полным Unicode, поэтому некоторые символы не могут быть представлены.
Вот пример использования, который дает мне печаль: у меня включено перетаскивание файлов на .py файлы в Проводнике Windows. У меня есть имена файлов со всеми типами символов, в том числе некоторые не на системной странице кода по умолчанию. Мой Python script не получает правильные имена файлов Unicode, переданные ему через sys.argv во всех случаях, когда символы не отображаются в текущей кодировке кодовой страницы.
Конечно, есть некоторый Windows API для чтения командной строки с полным Unicode (и Python 3.0 делает это). Я предполагаю, что интерпретатор Python 2.x не использует его.
Ответы
Ответ 1
Вот решение, которое именно я ищу, делая вызов функции 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
кажется счастливым разобрать его, что отлично.
Ответ 2
Работа с кодировками очень запутанна.
Я считаю, что если вы вводите данные через командную строку, они будут кодировать данные как независимо от вашей системной кодировки и не являются unicode. (Даже копирование/вставка должны делать это)
Таким образом, должно быть правильно декодировать в 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())
работает следующий вывод: Подсказкa > 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" содержится текст "日本語".
(Я закодировал файл как utf8, используя блокнот Windows, я немного зациклен на том, почему в начале печати появляется "?". Что-то связано с тем, как блокнот сохраняет utf8?)
Метод декодирования строк или встроенный unicode() может использоваться для преобразования кодировки в Юникод.
unicode_str = utf8_str.decode('utf8')
unicode_str = unicode(utf8_str, 'utf8')
Кроме того, если вы работаете с закодированными файлами, вы можете использовать функцию codecs.open() вместо встроенного open(). Он позволяет определить кодировку файла и затем использовать данную кодировку для прозрачного декодирования содержимого в Юникоде.
Поэтому, когда вы вызываете content = codecs.open("myfile.txt", "r", "utf8").read()
content
, он будет в Юникоде.
codecs.open:
http://docs.python.org/library/codecs.html?#codecs.open
Если я скучаю-понимаю что-то, пожалуйста, дайте мне знать.
Если вы еще не рекомендовали прочитать статью Джоэля о кодировке Unicode и кодировке:
http://www.joelonsoftware.com/articles/Unicode.html
Ответ 3
Попробуйте следующее:
import sys
print repr(sys.argv[1].decode('UTF-8'))
Возможно, вам нужно заменить CP437
или CP1252
на UTF-8
. Вы должны иметь возможность вывести правильное имя кодировки из раздела реестра HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage\OEMCP
Ответ 4
Командная строка может быть в кодировке Windows. Попробуйте декодировать аргументы в unicode
objects:
args = [unicode(x, "iso-8859-9") for x in sys.argv]