2009-11-27 6 views
3

Я использую ZipFile модуль питона, чтобы извлечь архив .zip (Давайте этот файл на http://img.dafont.com/dl/?f=akvaleir, например.)Извлечение файлов с недопустимыми символами в имени файла с Python

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

Его выход:

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

Оба файла недоступны после извлечения, поскольку в именах файлов есть недопустимые кодированные символы. Проблема в zipfile-модуле не имеет возможности указывать выходные имена файлов.

Однако «распакуйте akvaleir.zip» экранирует имя файла также:

[email protected]:~# unzip akvaleir.zip 
Archive: akvaleir.zip 
    inflating: AkvalВir_Normal_v2007.ttf 
    inflating: AkvalВir, La police - The Font - Fr - En.pdf 

Я попытался захватить выход «распакуйте -l akvaleir.zip» в моей питона программе, и эти два имена файлов:

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

Как я могу получить правильное имя файла, как то, что делает команда unzip без захвата вывода «unzip -l akvaleir.zip»?

ответ

7

Вместо метода extract используйте метод open и сохраните полученный псевдофайл на диске под любым именем, которое вы хотите, например, с shutil.copyfileobj.

+0

@Alex, спасибо, все работает. Вы знаете, как избежать недопустимого имени файла в python так же, как распаковать? – jack

+2

+1 не используйте 'extract' или' extractall', если вы не проверили все имена файлов, так как это может вытолкнуть файлы в любом месте вашей файловой системы. – bobince

+0

@jack, конечно, похоже, что они кодируют его в utf-8, но я не знаю, какую кодировку использует сам zipfile - попробуйте распечатать реестр имен файлов, как это происходит с Python из zipfile, Посмотрим, можем ли мы догадаться, что кодировка тоже (в основном вы будете декодировать имя файла в unicode, с любым кодеком, который он использует, а затем закодировать его в utf-8 для сохранения файла). –

8

Понадобилось некоторое время, но я думаю, что нашел ответ.

Я предположил, что это слово должно быть Akvaléir. Я нашел описание страницы об этом на французском языке. Когда я использовал фрагмент кода я имел строку как

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

Это не работает в UTF8, Latin-1, CP-1251 или CP-1252 кодировок. Затем я обнаружил, что CP863 является возможной канадской кодировкой, поэтому, возможно, это было из французской Канады.

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

Однако, я тогда прочитал Zip file format specification, который говорит

Формат ZIP исторически поддерживается только оригинальный IBM PC набор символов кодирования, обычно называют IBM Code Page 437.

...

Если установлен бит 11 общего назначения, имя файла и комментарий должны поддерживать стандарт Unicode, версия 4.1.0 или больше, используя кодировку символов , определенную в соответствии с конфигурацией UTF-8 .

Тестирование, что из дает мне тот же ответ, что и канадской кодовой страницы

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

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

На моем Mac, что дает

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

который закладки завершает в

ls Akvale\314\201ir 

и показывает с хорошей 'E' в моем файловом браузере.

+2

Да, вы должны знать исходную кодировку заранее. Формат ZIP не содержит абсолютно никакой информации, из которой вы можете определить, какие имена файлов кодирования используются. В то время как Mac и самые современные Linuxen разумно используют UTF-8 для своих файловых систем и внутри ZIP, машины Windows используют страницу системного кода, которая зависит от языка и никогда не UTF-8. Это настоящая головная боль. – bobince

+0

@ dalke, спасибо за информацию. Как определить, какую кодировку использует строка в программе python? – jack

+1

Прочтите спецификацию и выясните, что ZIP использует cp437 или utf-8. Существует флаг (бит 11), который указывает, какой из этих двух был использован. Я не написал код, чтобы проверить это. –

0

У меня возникла аналогичная проблема при запуске приложения с помощью Docker. Добавление этой строки в Dockerfile, фиксированную для меня все:

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 

Так что, я думаю, если вы не используете Docker, дайте ему попробовать и убедиться, что локали правильно генерируются и устанавливаются.

Смежные вопросы