29

次のディレクトリ構造を含む zip ファイルがあります。

dir1\dir2\dir3a
dir1\dir2\dir3b

解凍してディレクトリ構造を維持しようとしていますが、エラーが発生します:

IOError: [Errno 2] No such file or directory: 'C:\\\projects\\\testFolder\\\subdir\\\unzip.exe'

testFolder は上記の dir1 で、subdir は dir2 です。

ファイルを解凍してディレクトリ構造を維持する簡単な方法はありますか?

4

9 に答える 9

23

Python 2.6を使用している場合は、extractメソッドとextractallメソッドが最適です。今のところPython2.5を使用する必要があるため、ディレクトリが存在しない場合はディレクトリを作成する必要があります。メソッドを使用してディレクトリのリストを取得できますnamelist()。ディレクトリは常にスラッシュで終わります(Windowsでも)。

import os, zipfile

z = zipfile.ZipFile('myfile.zip')
for f in z.namelist():
    if f.endswith('/'):
        os.makedirs(f)

おそらくそのようにしたくないでしょう(つまり、名前リストを反復処理するときにzipファイルの内容を抽出したいでしょう)が、あなたはその考えを理解します

于 2009-03-12T19:11:36.257 に答える
16

extract() や extractall() を信用しないでください。

これらのメソッドは、ファイル名で指定されたパスにファイルをやみくもに抽出します。しかし、ZIP ファイル名は「x/../../../etc/passwd」のような危険な文字列を含め、何でもかまいません。そのようなファイルを抽出すると、サーバー全体が危険にさらされる可能性があります.

おそらく、これは Python の zipfile モジュールの報告可能なセキュリティ ホールと見なされるべきですが、過去にまったく同じ動作を示した zip-dearchiver は数多くあります。フォルダ構造を持つ ZIP ファイルを安全に解凍するには、各ファイル パスを詳細にチェックする必要があります。

于 2009-03-13T00:56:12.760 に答える
8

これを試してみて、再現できます。他の回答で示唆されているように、extractallメソッドは問題を解決しません。これは、zipファイルがどのように構造化されているかを誤解していない限り、私にはzipfileモジュールのバグのように見えます(おそらくWindowsのみですか?)。

testa\
testa\testb\
testa\testb\test.log
> test.zip

>>> from zipfile import ZipFile
>>> zipTest = ZipFile("C:\\...\\test.zip")
>>> zipTest.extractall("C:\\...\\")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "...\zipfile.py", line 940, in extractall
  File "...\zipfile.py", line 928, in extract
  File "...\zipfile.py", line 965, in _extract_member
IOError: [Errno 2] No such file or directory: 'C:\\...\\testa\\testb\\test.log'

を実行すると、次のprintdir()ようになります(最初の列):

>>> zipTest.printdir()
File Name
testa/testb/
testa/testb/test.log

次のように、最初のエントリだけを抽出しようとすると、次のようになります。

>>> zipTest.extract("testa/testb/")
'C:\\...\\testa\\testb'

ディスク上では、これによりtestaファイル testbが内部にあるフォルダが作成されます。test.logこれが、その後の抽出の試みが失敗する理由であると思われます。testa\testbはファイルであり、フォルダではありません。

編集#1:ファイルだけを抽出すると、機能します。

>>> zipTest.extract("testa/testb/test.log")
'C:\\...\\testa\\testb\\test.log'

編集#2:ジェフのコードが進むべき道です。繰り返しますnamelist; ディレクトリの場合は、ディレクトリを作成します。それ以外の場合は、ファイルを抽出します。

于 2009-03-12T19:11:42.667 に答える
6

これを言うのは少し遅いかもしれませんが、ジェフは正しいです。次のように簡単です。

import os
from zipfile import ZipFile as zip

def extractAll(zipName):
    z = zip(zipName)
    for f in z.namelist():
        if f.endswith('/'):
            os.makedirs(f)
        else:
            z.extract(f)

if __name__ == '__main__':
    zipList = ['one.zip', 'two.zip', 'three.zip']
    for zip in zipList:
        extractAll(zipName)
于 2011-06-25T14:32:43.357 に答える
3

Python 2.6を使用している場合は、extractallメソッドという非常に簡単な方法があります。

ただし、zipfileモジュールはC拡張機能なしで完全にPythonで実装されているため、2.6インストールからコピーして、古いバージョンのPythonで使用できます。これは、機能を自分で再実装するよりも簡単な場合があります。ただし、関数自体は非常に短いです。

def extractall(self, path=None, members=None, pwd=None):
    """Extract all members from the archive to the current working
       directory. `path' specifies a different directory to extract to.
       `members' is optional and must be a subset of the list returned
       by namelist().
    """
    if members is None:
        members = self.namelist()

    for zipinfo in members:
        self.extract(zipinfo, path, pwd)
于 2009-03-12T19:03:04.717 に答える
2

zipを解凍するためにunzipを実行しようとしているようです。

Pythonモジュールを使用する方がよいため、Pythonzipfileで抽出を行います。

import zipfile

def extract(zipfilepath, extractiondir):
    zip = zipfile.ZipFile(zipfilepath)
    zip.extractall(path=extractiondir)
于 2009-03-12T19:03:11.160 に答える
2

名前リストをフィルターしてフォルダーを除外する

namelist()で終わるエントリを除外するだけ/で、問題は解決します。

  z.extractall(dest, filter(lambda f: not f.endswith('/'), z.namelist()))

nJoy!

于 2012-08-26T12:20:07.567 に答える
1

zip ファイルには、ファイルだけでなくディレクトリのエントリも含まれる場合があることに注意してください。コマンドを使用してアーカイブを作成するときは、アーカイブへのディレクトリ エントリの明示的な追加を無効にするオプションをzip渡します。-DPython 2.6 のZipFile.extractallメソッドがディレクトリ エントリ全体で実行されると、その場所にファイルが作成されるようです。アーカイブ エントリは必ずしも順番通りに並んでいるとは限らないためZipFile.extractall、ファイルのサブディレクトリにファイルを作成しようとするため、頻繁に失敗します。Python モジュールで使用したいアーカイブがある場合は、単純にそれを解凍し、-Dオプションで再圧縮します。これは、まさにそれを行うためにしばらく使用してきた小さなスニペットです。

P=`pwd` && 
Z=`mktemp -d -t zip` && 
pushd $Z && 
unzip $P/<busted>.zip && 
zip -r -D $P/<new>.zip . && 
popd && 
rm -rf $Z

<busted>.zipand<new>.zipを、現在のディレクトリに相対的な実際のファイル名に置き換えます。次に、全体をコピーしてコマンド シェルに貼り付けるだけで、Python 2.6 ですぐに使用できる新しいアーカイブが作成されます。解凍せずにこれらのディレクトリ エントリを削除するコマンドがありますzip、IIRC では異なるシェル環境または zip 構成で奇妙な動作をしました。

于 2010-02-27T02:27:24.963 に答える