PythonChangle 6- ZipFile

最近在学习Python,看完了基础的部分感觉到应用欠佳,就想找个项目进行实践,在网上发现了Python Changle这个小的项目。开始闯关时一头雾水,步步艰难,还是跟没有很好的掌握其编程的思想吧。到第六关后,实在是无法突破,只好在网上求助找些答案。以下是找到的解决方案及运用的知识点。

Python zipfile模块介绍

zip是一种常见的压缩格式,Python中提供了zipfile模块来支持zip格式压缩文件的操作,包括文件压缩、解压缩、查看压缩文件信息等,还支持加密文件的解密(不过解密速度慢,因为是使用Python完成,而非C语言),zipfile目前不支持文件的加密。通过设置zipfile.ZipFile()函数的allowZip64参数为True,可以支持大小超过4G的zip文件的操作。

zipfile模块包含的内容主要如下:

  • exception zipfile.BadZipfile:当zip文件破损时抛出的异常
  • exception zipfile.LargeZipfile:当文件过大,而且allowZip64也没有设置为True时,抛出此异常
  • class zifile.ZipFile:处理zip文件的类。用来打开、读写zip文件
  • class zipfile.ZipInfo( [ filename [, date_time ] ] ):代表压缩文件中的一个被压缩的文件的类。ZipFile类的getinfo()和infolist()函数返回的是ZipInfo类的对象实例。通常,ZipInfo类的具体对象不需要我们创建,而是由函数返回得到。filename是zip文件的包含路径的完整文件名,date_time是一个包含了文件最新修改时间的有6个元素“年-月-日-时-分-秒”的元组(tuple),如(1999, 9, 11, 20, 11, 8)。
  • zipfile.is_zipfile(filename):基于文件的幻数(magic number,用来标记文件的格式,如rar格式文件的开头为Rar,zip文件开头为PK)判断文件filename是否是有效的zip文件,是则返回True,否则返回False。
  • zipfile.ZIP_STORED:代表未解压的压缩文件中的文件的数字常量。(The numeric constant for an uncompressed archive member.)
  • zipfile.ZIP_DEFLATED:代表通常压缩方法的数字常量。这个需要zlib模块的支持。目前还不支持其它的压缩方法。【deflate是一个压缩算法。它是无损压缩】

ZipFile类

ZipFile类用来处理zip文件,提供打开、解压缩等。ZipFile类的主要内容有:

class zipfile.ZipFile(file [,mode [,compression [,allowZip64] ] ])

ZipFile类构造函数,打开一个zip文件,并返回该zip文件的ZipFile对象。

  • file:要打开的zip文件的文件名,可以是一个普通的文件名(一个string),或者是一个类文件(file-like)的对象。
  • mode:文件打开方式。’r’表示读取已存在的文件,’w’表示截取(即存在则清空)并写一个新文件, ‘a’表示追加到一个已存在的文件后面(如果file指向的不是一个zip文件,那么将会创建一个新的zip文件,并且附加到file指向的文件,示例如下)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#coding=utf-8
#author=JarvisChu
 
import zipfile
 
#创建zip文件,使用'a'追加模式,同时file指向一个已存在的非zip文件
zf = zipfile.ZipFile( 'flower.jpg' , 'a' ,zipfile.ZIP_DEFLATED)
 
#创建一个文本文件 1.txt
f = open ( '1.txt' , 'w' )
f.write( 'This a simple text file' )
f.close()
 
#将文本文件附加到flower中
zf.write( '1.txt' )
 
#关闭zip文件
zf.close()

使用’a’模式打开了一张jpg图片,然后创建了一个1.txt文本文件并写入到压缩文件。运行上述脚本,则将包含1.txt的压缩文件附加到了flower.jpg图片文件中。此时,磁盘中直接打开flower.jpg则显示图片,如果改后缀名jpg为zip,则变为一个zip文件,打开后为包含了1.txt的压缩文件。

PythonChangle 6- ZipFile_第1张图片PythonChangle 6- ZipFile_第2张图片

这就是图片种子,原理就是将一个zip压缩文件附加到,或称为隐藏到一个图片文件中。在Windows系统中,可以另一种简单的制作方法:比如要将b.zip文件隐藏到a.jpg图片中,得到的图片种子命名为c.jpg,则打开cmd,切换到文件所在目录,输入下面一行命令即可

copy a.jpg /B + b.zip = c.jpg

c.jpg显示为a.jpg的图片,如果改后缀为zip,则变成了显示b.zip压缩文件。继续回到正文:

  • compression:当网往zip文件中写时使用的压缩算法,必须是ZIP_STORED或者ZIP_DEFLATED。如果该参数不是这两个可选值,则抛出RuntimeError异常。如果只为ZIP_DEFLATED,但是zlib模块不可用,则仍抛出RuntimeError异常。默认值为ZIP_STORED。如果allowZip64设为True,则当zip文件大于2GB时,会使用ZIP64扩展来创建zip文件。如果值为False(默认),当文件需要ZIP64扩展时,则会抛出异常。ZIP64扩展默认设置为无效,是因为Unix系统上zip和unzip命名不支持该扩展。

ZipFile.close()

关闭zip压缩文件。在程序退出时,必须调用close函数,否则一些必须的记录不会被写到文件中。

ZipFile.getinfo(name)

返回压缩文件中名为name的文件的ZipInfo对象。如果name文件不存在,则抛出KeyError异常

ZipFile.infolist()

返回一个包含了压缩文件中所有文件各自对应的ZipInfo对象的列表。列表中的排列顺序和在实际压缩文件中的排列顺序相同。

ZipFile.namelist()

返回一个包含了压缩文件中所有文件的名字的列表。

ZipFile.open(name [,mode [,pwd ] ])

解压压缩文件中一个文件得到类文件(file-like)对象。

  • name:压缩文件中的一个文件名或者是一个ZipInfo对象。
  • mode:必须是’r’、’U’或者’rU’。默认为’r’。选择’U’或者’rU’会使得只读对象中的universal newline功能生效。
  • pwd:解压密码。对一个关闭的ZipFile调用open()会抛出RuntimeError异常。

注1:file-like对象是只读的,且提供了如下函数:read()、readline()、readlines()、__iter__()、next()

注2:open()、read()和extract()函数,都接受一个文件名或者一个ZipInfo对象。当zip文件中存在重名的文件时,你会喜欢用这个方法的。

ZipFile.extract(member [,path [,pwd]])

解压zip文件中的文件member到当前工作目录。文件的信息会尽可能准确的被解压出来。path指示解压到另一个目录。pwd是解压密码。

ZipFile.extractall([path [,members [,pwd]]])

解压所有文件到当前工作目录。path指示解压到另一个目录。pwd是解压密码。

1
2
3
4
5
6
7
#coding=utf-8
import zipfile
zf = zipfile.ZipFile( 'simple.zip' , 'r' ,zipfile.ZIP_DEFLATED) #打开已存在的zip文件
zf.extractall() #解压到当前目录
#zf.extractall('..') #解压到上一级目录
#zf.extractall('C:/tmp')#解压到C:/tmp文件夹,文件夹不存在则创建
zf.close()

ZipFile.printdir()

以表格形式显示压缩文件中所有文件。

1
2
3
4
import zipfile
zf = zipfile.ZipFile( 'test.zip' , 'r' ,zipfile.ZIP_DEFLATED)
zf.printdir()
zf.close()

结果如下dir1为文件夹:

File Name                                      Modified                             Size

1.txt                                          2014-01-20 19:04:32           23

2.txt                                          2014-01-20 19:04:32           23

dir1/                                          2014-01-20 19:06:50            0

dir1/11.txt                             2014-01-20 19:06:26             0

dir1/12.txt                             2014-01-20 19:06:26             0

ZipFile.setpassword(pwd)

设置pwd为默认解压密码,用来解压加密的文件。

ZipFile.read(name [,pwd])

返回压缩文件中name文件的字节(bytes)。压缩文件必须是以read或者append模式打开。

  • name:文件名或ZipInfo对象
  • pwd:解压密码。如果指定了该参数,会覆盖sefpassword()中指定的默认解码密码。

对已经关闭的zip文件调用read()会产生一个RuntimeError的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# -*- coding: utf-8 -*-
import zipfile
#----------------------------------------------
# 解压文件示例
 
#打开压缩包
zf = zipfile.ZipFile( 'simple.zip' , 'r' )   #读取压缩包中的文件信息
for info in zf.infolist():
     print info.filename,info.date_time,info.file_size
     #输出: 1.txt (2014, 1, 2, 13, 16, 26) 23
 
     #解压文件
     for name in zf.namelist():  #文件名列表
         #print name   #1.txt,2.txt...
         f = file (name, 'w' )
         f.write(zf.read(name)) #read函数读取文件内容
         f.close()
 
#关闭压缩包
zf.close()

simple.zip中包含了1.txt,2.txt,3.txt,4.txt四个文件。zf.namelist()返回压缩文件中所有文件名列表。使用zf.read(name)读取文件中的所有字节,然后写入另一个文件则实现了解压。

ZipFile.testzip()

读取压缩包中所有文件,并检查它们的CRC和文件头。返回第一个破损的文件的名字,或者None。对已关闭的ZipFile对象调用testzip()会抛出RuntimeError异常。

ZipFile.write(filename [,arcname [,compress_type]]])

将文件名为filename的文件写入到压缩文件中。arcname指示在压缩包中文件重命名的名字,不指定则和原文件名相同。compress_type指示压缩类型,如果指定了该参数则会覆盖创建时的compression参数。压缩文件必须以write或append模式打开,否则抛出RuntimeError异常。对已关闭的ZipFile调用该函数也会抛出RuntimeError异常。

注意:官方没有规定的文件名的编码。如果你有一个unicode类型(相对str类型而言的)的文件名,你必须先转换成字节字符串(byte strings,即str类型的有编码的字符串),然后再传递给write()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: utf-8 -*-
import zipfile
 
#----------------------------------------------
# 压缩文件示例
 
txt = "This a simple text file"
 
#创建一个压缩文件
zf = zipfile.ZipFile( 'simple.zip' , 'w' ,zipfile.ZIP_DEFLATED)
 
#创建4个txt文件,(1.txt,2.txt...),并添加到压缩文件
for i in range ( 1 , 5 ):
     filename = str (i) + ".txt"
     f = file (filename, 'w' )
     f.write(txt)
     f.close()
     #添加到压缩文件
     zf.write(filename)
 
#关闭压缩文件
zf.close()

ZipFile.writestr(zinfo_or_arcname,bytes [,compress_type])

将字节串bytes写入到压缩包中。zinfo_or_arcname是压缩包中给定的文件名或者一个ZipInfo实例。如果是ZipInfo实例,至少要给出文件名、日期date和时间time。如果是文件名,date和time会被设置为当前的日期和时间。

注意:当将一个ZipInfo实例作为参数传入时,压缩函数使用ZipInfo实例中指定的compress_type。默认时,ZipInfo构造函数将这个参数设置w为ZIP_STORED.

ZipFile.debug

ZipFile类的数据成员。指示debug输出时使用的等级。可以设置为0-3。0为默认,表示无输出,3表示the most output。

ZipFile.comment

ZipFile类的数据成员。zip文件的注释信息。如果给使用’a’或’w’模式创建的ZipFile实例添加了注释,这个注释不能超过65535字节。超过这一大小的会被截断。【一个使用了comment的示例:PythonChallenge系列-P6 ZipFile】

PyZipFile类

PyZipFile的构造函数的参数与ZIpFile构造函数参数相同。PyZipFile类的实例比ZipFile对象实例多一个方法。

PyZipFile.writepy(pathname [,basename])

查找*.py文件并将其相关文件添加到压缩包中。如果存在*.pyo文件,则相关文件为*.pyo,否则为一个*.pyc文件,如果需要的话会编译该文件。如果pathname是一个以.py结尾的文件,则将相关文件(*.pyo 或*.pyc)添加到压缩包的最顶层,如果不是则抛出RuntimeError异常。如果pythname是一个目录,那么该目录下所有的*pyc和*pyo会被添加到压缩包最顶层,如果目录下有子目录,则递归添加。basename只用于内部使用。writepy()函数创建的文件名如下:

string.pyc                                # Top level name
test/__init__.pyc                         # Package directory
test/test_support.pyc                     # Module test.test_support
test/bogus/__init__.pyc                   # Subpackage directory
test/bogus/myfile.pyc                     # Submodule test.bogus.myfile

ZipInfo类

ZipInfo类的实例由ZipFile对象的getinfo()和infolist()函数返回。每一个对象值保存压缩包中一个文件的信息。ZipInfo对象有如下属性:

ZipInfo.filename

压缩包中的该文件名

ZipInfo.date_time

压缩包中该文件的最新修改时间,是一个包含6个元素的元组tuple。

Index Value
0 Year (>=1980)
1 Month (one-based)
2 Day of Month (one-based)
3 Hours (zero-based)
4 Minutes (zero-based)
5 Seconds (zero-based)

注意:不支持早于1980年的时间。

ZipInfo.compress_type

文件的压缩类型

ZipInfo.comment

文件的注释

ZipInfo.extra

附加数据。PKZIP Application Note给出了这个字符串中包含的结构体的一些注解。

ZipInfo.create_system

创建zip文件的系统

ZipInfo.create_version

创建zip文件的PKZIP版本

ZipInfo.extract_version

解压压缩包需要的PKZIP版本号

ZipInfo.reserved

必须是0

ZipInfo.bits

zip文件的标识位

ZipInfo.volume

文件头的卷号(volume number)

ZipInfo.internal_attr

文件的内部属性

ZipInfo.external_attr

文件的外部属性

ZipInfo.header_offset

文件头部的偏移字节数

ZipInfo.CRC

未压缩的文件的CRC-32

ZipInfo.compress_size

压缩数据的大小

ZipInfo.file_size

未压缩的文件的大小

结束

原文链接:zipfile — Work with ZIP archives。翻译过程中结合了自己的理解,并给出了一些示例代码。

作者:JarvisChu
原文链接:(译)Python zipfile模块介绍
版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0


解谜P5后跳转至第六题P6(http://www.pythonchallenge.com/pc/def/channel.html)

PythonChangle 6- ZipFile_第3张图片

just 一张图,注意到网页的标题为“Now there are pairs”, 看来是要用到pair数据结构,可是Python中并没有单独的这个类型的数据结构。继续,查看网页源代码,其中有如下一段话

The following has nothing to do with the riddle itself. I just thought it would be the right point to offer you to donate to the Python Challenge project. Any amount will be greatly appreciated. -thesamet

与谜题无关,让捐助的,作者的工作确实很值得肯定,捐助也是很应该的。鉴于目前我的情况,日后再说吧。

源码第一行有提示

看来这是与压缩相关,下载下来原图片,修改后缀名为为zip,无法打开,看来不是这个意思。<– 是什么意思?可能是问题的一个关键点吧。

先看看Python中的压缩相关知识,看有没有什么启发吧。

Python中有一个zip函数,将两个列表对应位置元素合成一个元组tuple,并返回所有tuple的列表。zip函数得到的正是一对对的pair,看来有戏。

1
2
3
4
5
6
7
8
>>> x = [ 1 , 2 , 3 ]
>>> y = [ 4 , 5 , 6 ]
>>> zipped = zip (x, y)
>>> zipped
[( 1 , 4 ), ( 2 , 5 ), ( 3 , 6 )]
>>> x2, y2 = zip ( * zipped)
>>> x = = list (x2) and y = = list (y2)
True

但是到哪里去找两个列表呢,这个zip也不是压缩啊。问题在哪?万般无奈,只能从url下手,将channel.html 换成zip.html,不错,页面提示一行文字

Yes, find the zip

看来有戏,再将channel.html换成channel.zip。很好,是一个zip文件下载。

PythonChangle 6- ZipFile_第4张图片

解压,打开readme.txt,内容为

welcome to my zipped list.

hint1: start from 90052

hint2: answer is inside the zip

在打开90052.txt,内容为

Next nothing is 94191

看来和第四题P4差不多了。不过这次换成了文件而已。

思路

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding: utf-8 -*-
 
nothing = "90052"
 
while True :
     filename = "channel/" + nothing + ".txt"
     print filename
     f = file (filename)
     line = f.readline()
     print line
     nothing = line.rsplit()[ - 1 ]
 
#最后一个打开的文件为46145.txt,内容为“Collect the comments.”

comment,注释? google一下 zip file comment,发现如下解释

A comment is optional text information that is embedded in a Zip file. It can be viewed, created, edited, or deleted using the Comment window. Simply type in your text or use any common Windows cut, copy, or paste method. This feature is available for Zip files only (.zip or .zipx).【1】

Zip格式的文件有一个comment信息。

再google  “zipfile comment python” ,第一个结果就是Python文档 zipfile — Work with ZIP archives。这里面讲解了zipfile的用法。

首先下载channel.zip文件,放到当前目录下,使用zipfile模块中的ZipFile方法打开channel.zip(代码如下),返回一个ZipFile类对象zip,zip.infolist()返回一个list,包含zip压缩包中每个文件的ZipInfo对象,ZipInfo对象存储了文件相关信息(filename,date_time,compress_type,comment,file_size等等)。遍历该list,由nothing构造出一个文件名,使用zip.getinfo()函数获取文件的ZipInfo,继而获取文件的comment,并追加到comments列表中;使用zip.read()读取文件中的内容,并分割得到下一个nothing的值。最后,使用join函数把comment连接成一个字符串并输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# -*- coding: utf-8 -*-
import zipfile
 
nothing = '90052'
comments = []
 
zip = zipfile.ZipFile( 'channel.zip' , 'r' )
try :
     for info in zip .infolist():
         filename = nothing + ".txt"
         comments.append( zip .getinfo(filename).comment) #读取每个文件的comment
         nothing = zip .read(filename).rsplit()[ - 1 ] #读取下一个nothing
except :
     print comments
print "".join(comments) #连接成一个字符串

输出结果为:

PythonChangle 6- ZipFile_第5张图片

hockey,曲棍球,冰球。

将url中的“channel.html” 替换成“hockey.html”,页面输出

it’s in the air. look at the letters.

让我们看单词,我们发现上面输出的”HOCKEY“ 分别由O、X、Y、G、E、N组成。所以单词是oxygen。再讲url中的hockey替换成oxygen。okay,问题解决。

解谜

将channel.html换成channel.zip,得到zip文件。依据zip文件comment,得到hockey.html,再将hockey.html替换成oxygen.html,成功解谜并打开下一题

http://www.pythonchallenge.com/pc/def/oxygen.html

Python知识点

  • zipfile模块用来处理zip压缩文件
  • zipfile.ZipFile类用来读、写zip压缩文件
  • ZipFile.get_info()获取压缩文件中一个文件的ZipInfo对象
  • ZipInfo对象包含了文件的相关信息(filename,date_time,compress_type,file_size等)

压缩文件

创建了一个simple.zip压缩文件,并将四个txt文件添加到压缩包内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: utf-8 -*-
import zipfile
 
#----------------------------------------------
# 压缩文件示例
 
txt = "This a simple text file"
 
#创建一个压缩文件
zf = zipfile.ZipFile( 'simple.zip' , 'w' ,zipfile.ZIP_DEFLATED)
 
#创建4个txt文件,(1.txt,2.txt...),并添加到压缩文件
for i in range ( 1 , 5 ):
     filename = str (i) + ".txt"
     f = file (filename, 'w' )
     f.write(txt)
     f.close()
     #添加到压缩文件
     zf.write(filename,)
 
#关闭压缩文件
zf.close()

PythonChangle 6- ZipFile_第6张图片

解压文件

使用zipfile.ZipFile(name,’r’)打开一个zip压缩包;遍历infolist列表获取压缩包汇中的每个文件的信息;遍历namelist列表获取压缩包中每个文件名;使用read()函数读取压缩文件的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: utf-8 -*-
import zipfile
#----------------------------------------------
# 解压文件示例
 
#打开压缩包
zf = zipfile.ZipFile( 'simple.zip' , 'r' )
 
#读取压缩包中的文件信息
for info in zf.infolist():
     print info.filename,info.date_time,info.file_size
     #输出: 1.txt (2014, 1, 2, 13, 16, 26) 23
 
#解压文件
for name in zf.namelist():  #文件名列表
     #print name   #1.txt,2.txt...
     f = file (name, 'w' )
     f.write(zf.read(name)) #read函数读取文件内容
     f.close()
 
#关闭压缩包
zf.close()


你可能感兴趣的:(Python)