Extrae archivos con caracteres no válidos en el nombre de archivo con Python

Utilizo el módulo zipfile de python para extraer un archivo .zip (Tomemos este archivo en http://img.dafont.com/dl/?f=akvaleir por ejemplo)

f = zipfile.ZipFile('akvaleir.zip', 'r') for fileinfo in f.infolist(): print fileinfo.filename f.extract(fileinfo, '.') 

Su salida:

 Akval ir_Normal_v2007.ttf Akval ir, La police - The Font - Fr - En.pdf 

Ambos archivos son inaccesibles después de la extracción porque hay caracteres codificados no válidos en sus nombres de archivo. El problema es que el módulo zipfile no tiene una opción para especificar nombres de archivos de salida.

Sin embargo, “descomprimir akvaleir.zip” escapa bien al nombre de archivo:

 root@host:~# unzip akvaleir.zip Archive: akvaleir.zip inflating: AkvalВir_Normal_v2007.ttf inflating: AkvalВir, La police - The Font - Fr - En.pdf 

Intenté capturar la salida de “unzip -l akvaleir.zip” en mi progtwig python y estos dos nombres de archivo son:

 Akval\xd0\x92ir_Normal_v2007.ttf Akval\xd0\x92ir, La police - The Font - Fr - En.pdf 

¿Cómo puedo obtener el nombre de archivo correcto como lo que hace el comando descomprimir sin capturar el resultado de “unzip -l akvaleir.zip”?

En lugar del método de extract , use el método open y guarde el pseudofile resultante en el disco con el nombre que desee, por ejemplo con shutil.copyfileobj .

Tomó un tiempo pero creo que encontré la respuesta.

Supuse que la palabra se suponía que era Akvaléir. Encontré una descripción de la página sobre eso, en francés. Cuando usé tu fragmento de código tuve una cadena como

 >>> fileinfo.filename 'Akval\x82ir, La police - The Font - Fr - En.pdf' >>> 

Eso no funcionó en las codificaciones UTF8, Latin-1, CP-1251 o CP-1252. Luego descubrí que el CP863 era una posible encoding canadiense, por lo que quizás fuera del Canadá francés.

 >>> print unicode(fileinfo.filename, "cp863").encode("utf8") Akvaléir, La police - The Font - Fr - En.pdf >>> 

Sin embargo, luego leí la especificación de formato de archivo Zip que dice

El formato ZIP ha admitido históricamente solo el conjunto de encoding de caracteres de PC original de IBM, comúnmente denominado página de códigos de IBM 437.

Si se establece el bit 11 de propósito general, el nombre de archivo y el comentario deben ser compatibles con el estándar de Unicode, versión 4.1.0 o superior, utilizando la forma de encoding de caracteres definida por la especificación de almacenamiento UTF-8.

Probar eso me da la misma respuesta que la página de códigos canadiense

 >>> print unicode(fileinfo.filename, "cp437").encode("utf8") Akvaléir, La police - The Font - Fr - En.pdf >>> 

No tengo un archivo zip codificado en Unicode y no voy a crear uno para averiguarlo, así que asumiré que todos los archivos zip tienen la encoding cp437.

 import shutil import zipfile f = zipfile.ZipFile('akvaleir.zip', 'r') for fileinfo in f.infolist(): filename = unicode(fileinfo.filename, "cp437") outputfile = open(filename, "wb") shutil.copyfileobj(f.open(fileinfo.filename), outputfile) 

En mi Mac que da

  109936 Nov 27 01:46 Akvale??ir_Normal_v2007.ttf 25244 Nov 27 01:46 Akvale??ir, La police - The Font - Fr - En.pdf 

que pestaña-completa a

 ls Akvale\314\201ir 

y aparece con un bonito ‘é’ en mi buscador de archivos.

Me encontré con un problema similar mientras ejecutaba mi aplicación usando Docker. Añadiendo estas líneas al archivo Docker, arreglé todo para mí:

 RUN locale-gen en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 

Entonces, supongo que si no estás usando Docker, pruébalo y asegúrate de que los locales se generen y configuren correctamente.