Extrair arquivos com caracteres inválidos no nome do arquivo com Python
Pergunta
Eu uso o módulo zipfile de python para extrair um arquivo .zip (Vamos dar esse arquivo em http :?. //img.dafont.com/dl/ f = akvaleir por exemplo)
f = zipfile.ZipFile('akvaleir.zip', 'r')
for fileinfo in f.infolist():
print fileinfo.filename
f.extract(fileinfo, '.')
Sua saída:
Akval�ir_Normal_v2007.ttf
Akval�ir, La police - The Font - Fr - En.pdf
Ambos os arquivos estão unaccessable após a extração, porque há caracteres codificados inválidos em seus nomes. O problema é o módulo zipfile não tem uma opção para especificar nomes de arquivos de saída.
No entanto, "unzip akvaleir.zip" escapa o nome do arquivo bem:
root@host:~# unzip akvaleir.zip
Archive: akvaleir.zip
inflating: AkvalВir_Normal_v2007.ttf
inflating: AkvalВir, La police - The Font - Fr - En.pdf
Eu tentei capturar saída do "unzip -l akvaleir.zip" no meu programa python e estes dois nomes são:
Akval\xd0\x92ir_Normal_v2007.ttf
Akval\xd0\x92ir, La police - The Font - Fr - En.pdf
Como posso obter o nome do arquivo correto como o comando unzip faz sem capturar saída do "unzip -l akvaleir.zip"?
Solução
Em vez do método extract
, utilize o método open
e salvar o pseudofile resultando no disco com o nome que você quiser, por exemplo, com shutil.copyfileobj
.
Outras dicas
Levou algum tempo, mas eu acho que encontrei a resposta.
Eu assumi a palavra deveria ser Akvaléir. Eu encontrei uma descrição da página sobre isso, em francês. Quando eu usei o trecho de código que eu tinha uma string como
>>> fileinfo.filename
'Akval\x82ir, La police - The Font - Fr - En.pdf'
>>>
Isso não funcionou em UTF8, Latin-1, CP-1251 ou CP-1252 codificações. Eu, então, descobriu que CP863 foi uma possível codificação canadense, talvez isso foi do francês no Canadá.
>>> print unicode(fileinfo.filename, "cp863").encode("utf8")
Akvaléir, La police - The Font - Fr - En.pdf
>>>
No entanto, eu então leia o formato de arquivo Zip especificação que diz
O formato ZIP tem historicamente suportado apenas o original IBM PC codificação de caracteres conjunto, comumente referido como Código IBM página 437.
...
Se propósito geral bit 11 é definido, o filename e comentários devem suportar o Padrão Unicode, Versão 4.1.0 ou maior usando a codificação de caracteres forma definida pelo armazenamento UTF-8 especificação.
Testing isso me dá a mesma resposta que a página de código canadense
>>> print unicode(fileinfo.filename, "cp437").encode("utf8")
Akvaléir, La police - The Font - Fr - En.pdf
>>>
Eu não tenho um Unicode codificado zip arquivo e eu não estou indo para criar um para descobrir, então eu vou assumir que todos os arquivos zip tem a codificação 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)
No meu Mac que dá
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 guia-concluída a
ls Akvale\314\201ir
e aparece com um bom 'é' no meu navegador de arquivos.
Eu corri para um problema semelhante durante a execução meu aplicativo usando Docker. Adicionando estas linhas ao Dockerfile, tudo fixo para mim:
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
Então, eu acho que se você não está usando Docker, experimentá-lo e certifique-se locais estão devidamente gerado e set.