Lea los caracteres Unicode de los argumentos de la línea de comandos en Python 2.x en Windows

Quiero que mi script de Python pueda leer los argumentos de la línea de comandos de Unicode en Windows. Pero parece que sys.argv es una cadena codificada en alguna encoding local, en lugar de Unicode. ¿Cómo puedo leer la línea de comandos en Unicode completo?

Código de ejemplo: 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) 

En mi PC configurada para la página de códigos en japonés, obtengo:

 C:\temp>argv.py "PC・ソフト申請書08.09.24.doc" PC・ソフト申請書08.09.24.doc  50438145835c83748367905c90bf8f9130382e30392e32342e646f63  

Eso es Shift-JIS codificado, creo, y “funciona” para ese nombre de archivo. Pero se rompe para los nombres de archivo con caracteres que no están en el conjunto de caracteres Shift-JIS; la llamada “abierta” final falla:

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

Nota: estoy hablando de Python 2.x, no de Python 3.0. He encontrado que Python 3.0 le da a sys.argv como Unicode adecuado. Pero aún es un poco pronto para la transición a Python 3.0 (debido a la falta de soporte de biblioteca de terceros).

Actualizar:

Algunas respuestas han dicho que debo decodificar de acuerdo con lo que se sys.argv codificado en sys.argv . El problema con eso es que no está lleno de Unicode, por lo que algunos caracteres no se pueden representar.

Aquí está el caso de uso que me duele: He habilitado arrastrar y soltar archivos en archivos .py en el Explorador de Windows . Tengo nombres de archivo con todo tipo de caracteres, incluidos algunos que no están en la página de códigos predeterminada del sistema. Mi secuencia de comandos de Python no obtiene los nombres de archivo de Unicode correctos a través de sys.argv en todos los casos, cuando los caracteres no se pueden representar en la encoding de la página de códigos actual.

Ciertamente hay algunas API de Windows para leer la línea de comandos con Unicode completo (y Python 3.0 lo hace). Asumo que el intérprete de Python 2.x no lo está utilizando.

Aquí hay una solución que es justo lo que estoy buscando, haciendo una llamada a la función GetCommandLineArgvW Windows:
Obtenga sys.argv con caracteres Unicode en Windows (desde ActiveState)

Pero he hecho varios cambios, para simplificar su uso y manejar mejor ciertos usos. Esto es lo que yo uso:

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() 

Ahora, la forma en que lo uso es simplemente hacer:

 import sys import win32_unicode_argv 

y de ahí en adelante, sys.argv es una lista de cadenas Unicode. El módulo optparse Python parece feliz de analizarlo, lo cual es genial.

Tratar con las codificaciones es muy confuso.

Creo que si ingresa los datos a través de la línea de comandos, los codificará como cualquiera que sea la encoding de su sistema y no es unicode. (Incluso copiar / pegar debería hacer esto)

Por lo tanto, debería ser correcto descodificar en Unicode utilizando la encoding del sistema:

 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()) 

ejecutando la siguiente salida de Will: Preguntar> python myargv.py “PC ・ ソ フ ト 申請書 08.09.24.txt”

 PC・ソフト申請書08.09.24.txt   PC・ソフト申請書08.09.24.txt  ?日本語 

Donde el “PC ・ ソ フ 08.09.24.txt” contenía el texto, “日本語”. (Codifiqué el archivo como utf8 con el bloc de notas de Windows, estoy un poco perplejo en cuanto a por qué hay un ‘?’ En el principio al imprimir. ¿Algo relacionado con cómo el bloc de notas guarda utf8?)

El método de ‘deencoding’ de cadenas o el builtin de unicode () se puede utilizar para convertir una encoding en unicode.

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

Además, si está tratando con archivos codificados, es posible que desee utilizar la función codecs.open () en lugar de la función abierta (). Le permite definir la encoding del archivo, y luego utilizará la encoding dada para decodificar de forma transparente el contenido a Unicode.

Por lo tanto, cuando llame a content = codecs.open("myfile.txt", "r", "utf8").read() content content = codecs.open("myfile.txt", "r", "utf8").read() estará en unicode.

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

Si estoy entendiendo mal algo, por favor hágamelo saber.

Si aún no lo ha recomendado, lea el artículo de Joel sobre Unicode y encoding: http://www.joelonsoftware.com/articles/Unicode.html

Prueba esto:

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

Quizás tenga que sustituir CP437 o CP1252 por UTF-8 . Debería poder inferir el nombre de encoding correcto de la clave de registro HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage\OEMCP

La línea de comando podría estar en la encoding de Windows. Intenta descodificar los argumentos en objetos unicode :

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