最近在学习Python,看完了基础的部分感觉到应用欠佳,就想找个项目进行实践,在网上发现了Python Changle这个小的项目。开始闯关时一头雾水,步步艰难,还是跟没有很好的掌握其编程的思想吧。到第六关后,实在是无法突破,只好在网上求助找些答案。以下是找到的解决方案及运用的知识点。
zip是一种常见的压缩格式,Python中提供了zipfile模块来支持zip格式压缩文件的操作,包括文件压缩、解压缩、查看压缩文件信息等,还支持加密文件的解密(不过解密速度慢,因为是使用Python完成,而非C语言),zipfile目前不支持文件的加密。通过设置zipfile.ZipFile()函数的allowZip64参数为True,可以支持大小超过4G的zip文件的操作。
zipfile模块包含的内容主要如下:
ZipFile类用来处理zip文件,提供打开、解压缩等。ZipFile类的主要内容有:
class zipfile.ZipFile(file [,mode [,compression [,allowZip64] ] ])
ZipFile类构造函数,打开一个zip文件,并返回该zip文件的ZipFile对象。
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的压缩文件。
这就是图片种子,原理就是将一个zip压缩文件附加到,或称为隐藏到一个图片文件中。在Windows系统中,可以另一种简单的制作方法:比如要将b.zip文件隐藏到a.jpg图片中,得到的图片种子命名为c.jpg,则打开cmd,切换到文件所在目录,输入下面一行命令即可
copy a.jpg /B + b.zip = c.jpg
c.jpg显示为a.jpg的图片,如果改后缀为zip,则变成了显示b.zip压缩文件。继续回到正文:
ZipFile.close()
关闭zip压缩文件。在程序退出时,必须调用close函数,否则一些必须的记录不会被写到文件中。
ZipFile.getinfo(name)
返回压缩文件中名为name的文件的ZipInfo对象。如果name文件不存在,则抛出KeyError异常
ZipFile.infolist()
返回一个包含了压缩文件中所有文件各自对应的ZipInfo对象的列表。列表中的排列顺序和在实际压缩文件中的排列顺序相同。
ZipFile.namelist()
返回一个包含了压缩文件中所有文件的名字的列表。
ZipFile.open(name [,mode [,pwd ] ])
解压压缩文件中一个文件得到类文件(file-like)对象。
注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模式打开。
对已经关闭的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的构造函数的参数与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类的实例由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)
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文件下载。
解压,打开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)
#连接成一个字符串
|
输出结果为:
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
压缩文件
创建了一个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()
|
解压文件
使用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()
|