web-dev-qa-db-ja.com

ZipFileモジュールを使用してzipfileからファイルを削除します

Zipファイルからファイルを削除するために思いついた唯一の方法は、削除するファイルを含まない一時的なzipファイルを作成してから、元のファイル名に名前を変更することでした。

python 2.4では、ZipInfoクラスには属性file_offset、したがって、2番目のZipファイルを作成し、解凍/再圧縮せずにデータを他のファイルにコピーすることが可能でした。

この file_offsetがpython 2.6にないので、すべてのファイルを解凍してから再度圧縮して別のzipファイルを作成する以外のオプションはありますか?

Zipファイル内のファイルを直接削除する方法はありますか?検索しても何も見つかりませんでした。

37
RSabet

次のスニペットは私のために働きました(Zipアーカイブからすべての* .exeファイルを削除します):

zin = zipfile.ZipFile ('archive.Zip', 'r')
zout = zipfile.ZipFile ('archve_new.Zip', 'w')
for item in zin.infolist():
    buffer = zin.read(item.filename)
    if (item.filename[-4:] != '.exe'):
        zout.writestr(item, buffer)
zout.close()
zin.close()

すべてをメモリに読み込むと、2番目のファイルが不要になります。ただし、このスニペットはすべてを再圧縮します。

綿密な検査の後、ZipInfo.header_offsetは、ファイルの開始からのオフセットです。名前は誤解を招く可能性がありますが、メインのZipヘッダーは実際にはファイルの最後に格納されています。私の16進エディタはこれを確認します。

したがって、発生する問題は次のとおりです。メインヘッダーのディレクトリエントリも削除する必要があります。そうしないと、存在しないファイルを指すことになります。削除するファイルのローカルヘッダーも保持している場合は、メインヘッダーをそのままにしておくと機能する可能性がありますが、それについてはよくわかりません。古いモジュールでどのようにそれを行いましたか?

メインヘッダーを変更せずに開くと、「zipファイルにXバイトがありません」というエラーが表示されます。 This は、メインヘッダーを変更する方法を見つけるのに役立つ場合があります。

43
mdm

あまりエレガントではありませんが、これが私がやった方法です:

import subprocess
import zipfile

z = zipfile.ZipFile(Zip_filename)

files_to_del = filter( lambda f: f.endswith('exe'), z.namelist()]

cmd=['Zip', '-d', Zip_filename] + files_to_del
subprocess.check_call(cmd)

# reload the modified archive
z = zipfile.ZipFile(Zip_filename)
6
Kurt

_delete_from_Zip_file_ ¹のルーチン_ruamel.std.zipfile_を使用すると、Zip内のフルパスに基づいて、または(re)パターンに基づいてファイルを削除できます。例えば。を使用して_.exe_からすべての_test.Zip_ファイルを削除できます

_from ruamel.std.zipfile import delete_from_Zip_file

delete_from_Zip_file('test.Zip', pattern='.*.exe')  
_

(_*_の前のドットに注意してください)。

これはmdmのソリューション(再圧縮の必要性を含む)と同様に機能しますが、メモリ内にZipファイルを再作成し(クラス InMemZipFile() を使用)、完全に読み取られた後に古いファイルを上書きします。


¹ 免責事項:私はそのパッケージの作者です。

1
Anthon