天问

  • Blog
  • 热修复与动态加载
  • python爬虫
  • 专栏
    • 热修复与动态加载
    • NDK开发
    • 设计模式与架构
    • React Native
  • 好文转载
    • 计算机技术
    • 投资理财
    • 摄影与拍照技巧
  • 投稿
  • 关于本站及隐私政策
  • Github
StevenQ
为今天工作,为明天投资,为未来孵化一些东西!
  1. 首页
  2. 计算机技术
  3. python
  4. 正文

python文件操作之zip包操作

2017年7月8日 1067点热度 1人点赞 0条评论

在平时的项目开发中,文件操作是非常频繁的一个场景,前面python文件操作中带领大家学些了基本的文件操作,但是还有一类特殊的文件就是压缩包文件,如jar包,apk包本质上都是压缩包,对这类文件处理要使用python中的zipfile模块。

首先来看一下ZipFile类的定义

zipfile.ZipFile

class zipfile.ZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True)

可以看到ZipFile包含四个参数,其中后面的三个参数都是自带默认值的参数,各个参数的含义如下:

  • file,文件的路径或者文件对象本身,如果是文件路径通常是一个字符串
  • mode,打开zip包的模式,和签名讲解的打开文件的模式一样
  • compression:压缩类型,因为zip包是压缩包,是采用某种压缩算法压缩后的文件格式,因此会存在不同的压缩类型,如ZIP_STORED, ZIP_DEFLATED, ZIP_BZIP2 or ZIP_LZMA等
  • allowZip64,该值默认为True,当zipfile大于4 GiB时,zipfile将创建使用ZIP64扩展名的ZIP文件

注意ZipFile和open一样也支持with as语句,因此好的习惯是通过with as语句来使用ZipFile,这样zipfile对象会自动调用close()函数来关闭读写操作当with语句块结束时,即使出现异常也会close(),这样能够避免我们忘记手动调用close()函数,像下面这样:

with zipfile.ZipFile(jarPath) as jar:
    jar.write('test.txt')

官方文档点这里:https://docs.python.org/3/library/zipfile.html

然后来看下ZipFile类常用的函数

ZipFile.namelist()

返回zip包所有的entry名称的list,注意返回的entry名称中包含目录路径,如X/Y/z.txt。

ZipFile.getinfo(name)

name为传入的entry的名称,注意需要包括目录路径,如某zip包解压后目录层级关系为X/Y/z.txt,则需传入X/Y/z.txt,若直接传入z.txt,因为该zip包中不存在该entry,因此会抛出KeyError错误,如果entry存在,则会返回和该entry名称对应的ZipInfo对象。ZipInfo对象中定义了很多有用的信息,如压缩文件的压缩类型,压缩后的大小,压缩前的大小。常用的属性及其含义如下:

  • ZipInfo.compress_type,该entry的压缩类型,常见的压缩类型包括ZIP_STORED和ZIP_DEFLATED
  • ZipInfo.compress_size,该entry压缩后的大小
  • ZipInfo.file_size,该entry压缩前的大小
  • ZipInfo.CRC ,该entry的CRC校验码

除了属性外,ZipInfo还包括一些很有用的函数,如ZipInfo.is_dir()函数可以判断该entry是目录还是文件,如果为目录返回True

ZipFile.extract(member, path=None, pwd=None)

解压出名称为member的entry到当前工作路径下,该函数各个参数含义如下:

  • member,待解压的entry名称,同样名称包含路径,如X/Y/z.txt
  • path,解压目录,默认值为None,解压到当前工作目录,如果指定了该值,则会解压到path路径下
  • pwd,解压的密码,如果该zip在压缩时进行过加密操作,则需要传入该值

ZipFile.write(filename, arcname=None, compress_type=None)

将名为filename的文件添加到zip包中,该函数各个参数含义如下:

  • filename,需要添加(写入)到zip包中的文件,通常是一个文件路径的字符串
  • arcname,添加到zip包中的文件的名称,默认为None,如果不指定该值,则默认采用和filename一样的名称
  • compress_type,压缩到zip包中时的压缩类型,如ZIP_STORED,ZIP_DEFLATED

 

虽然ZipFile为我们提供了很多API,但是很遗憾目前的python中ZipFile还未给我们提供一个不解压直接删除zip包中文件的函数,这里我们可以自己定义一个类来继承自ZipFile,然后自己实现直接删除zip包中某一个entry的函数,包括直接删除文件或者文件夹。代码如下:

import zipfile
from zipfile import ZipFile

class ZipManager(ZipFile):
    
    # 删除zip包中的文件,不能是文件夹
    def  remove(self, member):
        if "a" not in self.mode:
            raise RuntimeError('remove() requires mode "a"')
        if not self.fp:
            raise RuntimeError(
                "Attempt to modify ZIP archive that was already closed")

        # Make sure we have an info object
        if isinstance(member, zipfile.ZipInfo):
            # 'member' is already an info object
            zinfo = member
        else:
            # Get info object for member
            zinfo = self.getinfo(member)

        # To remove the member we need its size and location in the archive
        fname = zinfo.filename
        zlen = len(zinfo.FileHeader()) + zinfo.compress_size
        fileofs = zinfo.header_offset

        # Modify all the relevant file pointers
        for info in self.infolist():
            if info.header_offset > fileofs:
                info.header_offset = info.header_offset - zlen

        # Remove the zipped data
        self.fp.seek(fileofs + zlen)
        after = self.fp.read()
        self.fp.seek(fileofs)
        self.fp.write(after)
        self.fp.truncate()

        # Fix class members with state
        self._didModify = True
        self.filelist.remove(zinfo)
        del self.NameToInfo[fname]

    #删除zip包中的文件夹
    def delete_dir(self,dir_name):
        entrys = self.namelist()
        is_dir_existed=False
        for delete_entry in entrys:
            if delete_entry.startswith(dir_name):
                is_dir_existed=True
                self.remove(delete_entry)
        if not is_dir_existed:
            print 'entry dir  which you input is not existed'

在这里我们定义了一个ZipManager类继承自ZipFile类,然后自定义了remove函数用来直接删除zip包中的某一个文件,delete_dir函数来删除文件夹。这样对zip包的操作就比较完整了。

综合实例

这里实现一个操作就是替换zip包中某个文件,给一个apk文件X.apk,替换文件Y,将apk包中的Z替换为Y,要求替换后,Y的压缩类型要与原apk包中的文件保持一致。之所以选该实例,是因为很显然该实例包括了删除原zip包中的Z,添加Y到zip包中,也就是说该实例可以把ZipFile中的重要函数和属性都用上。当然要用到前面我们扩展的ZipManager类来实现直接删除某一项文件的操作。代码如下:

def replace_file_from_zip(zip_file_path, file_to_replace, entry_name):
    if not os.path.exists(zip_file_path) or not zip_file_path.endswith('.apk'):
        print "zipfile is not existed or it's not a apk file"
        return;
    if not os.path.exists(file_to_replace):
        print "file which you want to replace is not existed"
        return;
    apk = ZipManager(zip_file_path, 'a')
    if entry_name not in apk.namelist():
        print "can not find entryName file which you input in zip file or this it's a dir not a file"
        return;
    else:
        compress_type=apk.getinfo(entry_name).compress_type
        apk.remove(entry_name)
        apk.write(file_to_replace,entry_name,compress_type)
    apk.close()

在这里我们用到了ZipFile的getrinfo函数来获取ZipInfo对象,然后通过ZipInfo.compress_type属性来得到该entry的压缩类型,然后调用我们扩展的ZipManager的remove函数直接删除该entry,最后调用write函数向zip包中添加文件,同时指定添加的文件的名称和压缩类型,这个实例可以说是zip包操作中综合性最高的一个操作。

 

 

注:本文首次发表于huqi.tech,请勿转载,如需转载,请注明出处:huqi.tech

 

扫描下方二维码实时接收最新技术干货推送

扫描二维码实时接收最新技术干货推送,而且会不定期的发布互联网名企内推机会哦!

 

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: zip包操作 文件操作
最后更新:2017年7月8日

天问

追梦追自由,穷则独善其身,达则兼济天下

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2022 天问 ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang