9–1. 文件过滤.
显示一个文件的所有行, 忽略以井号( # )开头的行. 这个字符被用做Python , Perl, Tcl, 等大多脚本文件的注释符号.附加题: 处理不是第一个字符开头的注释.
try:
f = open('test.txt')
for item in f:
if item.startswith('#'):
continue
elif '#' in item:
index = item.rfind('#')
print item[0:index]
else:
print item,
except IOError, e:
print e
else:
f.close()
9–2. 文件访问.
提示输入数字 N 和文件 F, 然后显示文件 F 的前 N 行.
name = raw_input('file name: ')
n = int(raw_input('lines: '))
with open(name) as f:
for i, item in enumerate(f):
print item,
if i + 1 == n:
break
9–3. 文件信息.
提示输入一个文件名, 然后显示这个文本文件的总行数.
name = raw_input('file name: ')
with open(name) as f:
print len(f.readlines())
9–4. 文件访问.
写一个逐页显示文本文件的程序. 提示输入一个文件名, 每次显示文本文件的 25 行, 暂停并向用户提示"按任意键继续.", 按键后继续执行.
name = raw_input('file name: ')
page = 25
with open(name) as f:
for i, item in enumerate(f):
if (i + 1) % page == 0:
raw_input('Press any key to continue...')
print item,
9–6. 文件比较.
写一个比较两个文本文件的程序. 如果不同, 给出第一个不同处的行号和列号.
def diff(str1, str2):
for i in xrange(len(str1)):
if (i < len(str2)) and (str1[i] != str2[i]) or (i == len(str2)):
return i
def filecmp(file1, file2):
from os.path import getsize
if getsize(file1.name) == 0 or getsize(file2.name) == 0:
return (1, 1)
for i, item in enumerate(file1):
try:
string = file2.next()
if item != string:
col = diff(item, string) + 1
row = i + 1
return (row, col)
except StopIteration:
break
if __name__ == '__main__':
f1 = open(raw_input('the first file: '))
f2 = open(raw_input('the second file: '))
result = filecmp(f1, f2)
if not result:
print 'the same'
else:
print result
f1.close()
f2.close()
9–7. 解析文件.
Win32 用户: 创建一个用来解析 Windows .ini 文件的程序. POSIX 用户:创建一个解析 /etc/serves 文件的程序. 其它平台用户: 写一个解析特定结构的系统配置文件的程序.
section = {}
with open(r'c:\windows\win.ini') as f:
for item in f:
if item.startswith(';'):
continue
if item.startswith('['):
data = {}
name = item[1:item.rfind(']')]
section[name] = data
continue
if item.count('=') > 0 and data != None:
index = item.find('=')
key = item[0:index]
value = item[index+1:].strip()
data[key] = value
print section
9–8. 模块研究.
提取模块的属性资料. 提示用户输入一个模块名(或者从命令行接受输入).然后使用 dir() 和其它内建函数提取模块的属性, 显示它们的名字, 类型, 值.
name = raw_input('module name: ')
obj = __import__(name)
ls = dir(obj)
for item in ls:
print 'name: ', item
print 'type: ', type(getattr(obj, item))
print 'value: ', getattr(obj, item)
print
9–9. Python 文档字符串.
进入 Python 标准库所在的目录. 检查每个 .py 文件看是否有__doc__ 字符串, 如果有, 对其格式进行适当的整理归类. 你的程序执行完毕后, 应该会生成一个漂亮的清单. 里边列出哪些模块有文档字符串, 以及文档字符串的内容. 清单最后附上那些没有文档字符串模块的名字.附加题: 提取标准库中各模块内全部类(class)和函数的文档.
#get a list of modules
from os import chdir, listdir
path = r'c:\python27\Lib'
chdir(path)
ls = [item for item in listdir(path) if item.endswith('.py')]
db = {}.fromkeys(ls)
#scan __doc__ in modules
for item in ls:
with open(item) as f:
doc = ''
start = False
for line in f:
if line.strip().startswith('"""') and not start:
start = True
doc += line
if doc.strip().endswith('"""') and len(doc.strip()) > 3:
start = False
break
elif line.strip().endswith('"""'):
start = False
doc += line
break
elif start:
doc += line
db[item] = doc
empty = []
full = []
for key in sorted(db):
if db[key] == '':
empty.append(key)
else:
full.append(key)
print 'moudles without doc:'
for item in empty:
print item,
print
print 'moudles with doc:'
for item in full:
print item,
print
print 'values:'
for key in full:
print 'module: ', key
print 'doc: ', db[key]
print
9–11. Web 站点地址.
a) 编写一个 URL 书签管理程序. 使用基于文本的菜单, 用户可以添加, 修改或者删除书签数据项. 书签数据项中包含站点的名称, URL 地址, 以及一行简单说明(可选). 另外提供检索功能,可以根据检索关键字在站点名称和 URL 两部分查找可能的匹配. 程序退出时把数据保存到一个磁盘文件中去; 再次执行时候加载保存的数据.
b)改进 a) 的解决方案, 把书签输出到一个合法且语法正确的 HTML 文件(.html 或 htm )中,这样用户就可以使用浏览器查看自己的书签清单. 另外提供创建"文件夹"功能, 对相关的书签进行分组管理.
附加题: 请阅读 Python 的 re 模块了解有关正则表达式的资料, 使用正则表达式对用户输入的 URL 进行验证.
import shelve
def append_bookmark(ls, name, url, remark, group='default'):
assert isinstance(ls, list)
ls.append((name, url, remark, group))
def edit_bookmark(ls, index, name, url, remark, group='default'):
assert isinstance(ls, list)
ls[index] = (name, url, remark, group)
def delete_bookmark(ls, index):
assert isinstance(ls, list)
ls.pop(index)
def find_bookmark(ls, fname, furl):
assert isinstance(ls, list)
for i, item in enumerate(ls):
(name, url, remark, _) = item
if fname and furl :
if (furl in url) and (fname in name):
return i
if fname and (fname in name):
return i
if furl and (furl in url):
return i
else:
return -1
def output2html(ls, filename):
assert isinstance(ls, list)
fmt = '%d\t%s\t<a href=%s>%s</a>\t%s\t%s<br>'
with open(filename, 'w') as f:
f.write('<html><head><title>bookmark</title></head><body>')
for i, item in enumerate(ls):
(name, url, remark, group) = item
content = fmt % (i+1, name, r'http:\\' + url, url, remark, group)
f.write(content)
f.write('</body></html>')
def sort_list(ls):
ls.sort(lambda x, y: cmp(x[3], y[3]))
def save(ls, filename):
try:
db = shelve.open(filename, 'c')
db['data'] = ls
finally:
db.close()
def load(filename):
db = shelve.open(filename, 'r')
return db['data']
def print_all(ls):
for i, item in enumerate(ls):
print '%d\t%s\t%s\t%s\t%s' % (i+1, item[0], item[1], item[2], item[3])
def get_input():
name = raw_input('name; ')
url = raw_input('url: ')
remark = raw_input('remark: ')
group = raw_input('group: ')
return (name, url, remark, group)
def show_menu():
try:
ls = load('sdata.txt')
except:
ls = []
while True:
print '(A)ppend'
print '(E)dit'
print '(D)elete'
print '(F)ind'
print '(S)how'
print '(O)utput to html'
print '(Q)uit'
ch = raw_input('input your choice: ')[0].lower()
if ch not in 'aedfsbo':
save(ls, 'sdata.txt')
break
if ch == 'a':
data = get_input()
append_bookmark(ls, *data)
elif ch == 'e':
index = int(raw_input('index: '))
data = get_input()
edit_bookmark(ls, index, *data)
elif ch == 'd':
index = int(raw_input('index: '))
delete_bookmark(ls, index)
elif ch == 'f':
name = raw_input('name: ')
url = raw_input('url: ')
index = find_bookmark(ls, name, url)
if index == -1:
print 'not found'
else:
print ls[index]
elif ch == 's':
sort_list(ls)
print_all(ls)
elif ch == 'o':
sort_list(ls)
output2html(ls, 'bookmark.html')
if __name__ == '__main__':
show_menu()
9–13. 命令行参数
a) 什么是命令行参数, 它们有什么用?
b) 写一个程序, 打印出所有的命令行参数.
from sys import argv
for item in argv:
print item
9–15. 复制文件.
提示输入两个文件名(或者使用命令行参数). 把第一个文件的内容复制到第二个文件中去.
def copyfile(src, dst):
with open(dst, 'a') as f:
with open(src) as s:
for item in s:
f.write(item)
if __name__ == '__main__':
src = raw_input('copy from: ')
dst = raw_input('copy to: ')
copyfile(src, dst)
9–16. 文本处理.
人们输入的文字常常超过屏幕的最大宽度. 编写一个程序, 在一个文本文件中查找长度大于 80 个字符的文本行. 从最接近 80 个字符的单词断行, 把剩余文件插入到
下一行处.程序执行完毕后, 应该没有超过 80 个字符的文本行了.
from os import linesep
def nearest(words, maxlen):
index = -1
if len(words) > maxlen:
ch = words[maxlen-1]
if ch.isalpha() and not words[maxlen].isspace():
index = words.rfind(' ', 0, maxlen-1)
return index
def wrap(filename, maxlen=80):
with open(filename) as f:
ls = [item for item in f]
print [len(item) for item in ls]
for i, item in enumerate(ls):
if len(item) > maxlen:
index = nearest(item, maxlen)
if index == -1:
tail = item[maxlen:]
ls[i] = item[:maxlen]
else:
tail = item[index:]
ls[i] = item[:index]
if (i + 1 < len(ls)):
ls[i+1] = tail.strip() + ls[i+1]
else:
ls.append(tail)
string = linesep.join(ls)
print [len(item) for item in ls]
with open(filename, 'w') as f:
f.write(string)
if __name__ == '__main__':
filename = raw_input('file name: ')
wrap(filename)
9–17. 文本处理.
创建一个原始的文本文件编辑器. 你的程序应该是菜单驱动的, 有如下这些选项:
1) 创建文件(提示输入文件名和任意行的文本输入),
2) 显示文件(把文件的内容显示到屏幕),
3) 编辑文件(提示输入要修改的行, 然后让用户进行修改),
4) 保存文件, 以及
5) 退出.
def create(filename, content):
with open(filename, 'w') as f:
f.write(content)
def show(filename):
with open(filename) as f:
for i, item in enumerate(f):
print i, item,
def edit(filename, index, content):
assert filename != ''
with open(filename) as f:
ls = [item for item in f]
ls[index] = content
return ls
def save(filename, ls):
content = ''.join(ls)
with open(filename, 'w') as f:
f.write(content)
def showmenu():
filename = ''
ls = []
while True:
print '\n1.Create'
print '2.Show'
print '3.Edit'
print '4.Save'
print '5.quit'
ch = raw_input('input your choice: ')[0]
if ch not in '1234':
break
if ch == '1':
filename = raw_input('file name: ')
content = raw_input('content of file: ')
create(filename, content)
elif ch == '2':
filename = raw_input('file name: ')
show(filename)
elif ch == '3':
if filename == '':
filename = raw_input('file name: ')
index = int(raw_input('index: '))
content = raw_input('content: ')
ls = edit(filename, index, content)
elif ch == '4':
if len(ls) > 0:
save(filename, ls)
print 'saved succeed'
if __name__ == '__main__':
showmenu()
9–18. 搜索文件.
提示输入一个字节值(0 - 255)和一个文件名. 显示该字符在文件中出现的次数.
def counts(filename, value):
ch = chr(value)
with open(filename, 'rb') as f:
total = sum(item.count(ch) for item in f)
return total
if __name__ == '__main__':
filename = raw_input('file name: ')
value = int(raw_input('value: '))
print counts(filename, value)
9–19. 创建文件.
创建前一个问题的辅助程序. 创建一个随机字节的二进制数据文件, 但某一特定字节会在文件中出现指定的次数. 该程序接受三个参数:
1) 一个字节值( 0 - 255 ),
2) 该字符在数据文件中出现的次数, 以及
3) 数据文件的总字节长度.
你的工作就是生成这个文件, 把给定的字节随机散布在文件里, 并且要求保证给定字符在文件中只出现指定的次数, 文件应精确地达到要求的长度.
from random import randint
def create(filename, value, total, maxlen):
assert 0 <= value <= 255
ls = [chr(randint(0, 255)) for i in xrange(maxlen-total)]
ch = chr(value)
for i in xrange(total-ls.count(ch)):
ls.insert(randint(0, len(ls)-1), ch)
for i in xrange(maxlen - len(ls)):
ls.insert(randint(0, len(ls)-1), chr(randint(0, value-1)))
with open(filename, 'wb') as f:
f.write(''.join(ls))
if __name__ == '__main__':
filename = raw_input('file name: ')
value = int(raw_input('value: '))
total = int(raw_input('total: '))
maxlen = int(raw_input('max length of file: '))
create(filename, value, total, maxlen)
9–20. 压缩文件.
写一小段代码, 压缩/解压缩 gzip 或 bzip 格式的文件. 可以使用命令行下的 gzip 或 bzip2 以及 GUI 程序 PowerArchiver , StuffIt , 或 WinZip 来确认你的 Python支持这两个库.
import gzip
def compress(zipfile, filename):
obj = gzip.open(zipfile, 'wb')
with open(filename, 'rb') as f:
obj.writelines(f)
obj.close()
def decompress(zipfile, filename):
obj = gzip.open(zipfile, 'rb')
content = obj.read()
with open(filename, 'wb') as f:
f.write(content)
if __name__ == '__main__':
compress('test.gzip', 'wx.txt')
decompress('test.gzip', 'dc.txt')
9–21. ZIP 归档文件.
创建一个程序, 可以往 ZIP 归档文件加入文件, 或从中提取文件,有可能的话, 加入创建ZIP 归档文件的功能.
import zipfile
def add_file(zipname, filename):
with zipfile.ZipFile(zipname, 'a') as obj:
obj.write(filename)
def read_file(zipname, filename):
with zipfile.ZipFile(zipname, 'r') as obj:
content = obj.read(filename)
with open(filename, 'w') as f:
f.write(content)
if __name__ == '__main__':
add_file('answer1.zip', 'wx.txt')
read_file('answer1.zip', 'answer1.txt')
9–22. ZIP 归档文件.
unzip -l 命令显示出的 ZIP 归档文件很无趣. 创建一个 Python脚本 lszip.py , 使它可以显示额外信息: 压缩文件大小, 每个文件的压缩比率(通过比较压缩前后文件大小), 以及完成的 time.ctime() 时间戳, 而不是只有日期和 HH:MM .
提示: 归档文件的 date_time 属性并不完整, 无法提供给 time.mktime() 使用....这由你自己决定.
import zipfile
import os
import time
filename = raw_input('zip file name: ')
print 'zip file size: ', os.stat(filename).st_size
f = zipfile.ZipFile(filename, 'r')
print 'filename\tdatetime\tsize\tcompress size\trate'
for info in f.infolist():
t = time.ctime(time.mktime(tuple(list(info.date_time) + [0, 0, 0])))
print '%s\t%s\t%d\t%d\t%.2f%%' % (info.filename, t, info.file_size, info.compress_size, float(info.compress_size) / info.file_size * 100)
f.close()
9–23. TAR 归档文件.
为 TAR 归档文件建立类似上个问题的程序. 这两种文件的不同之处在于 ZIP 文件通常是压缩的, 而 TAR 文件不是, 只是在 gzip 和 bzip2 的支持下才能完成压缩工作. 加入任意一种压缩格式支持.
附加题: 同时支持 gzip 和 bzip2 .
import tarfile
import time
filename = raw_input('file name: ')
if not tarfile.is_tarfile(filename):
print "it's not a tarfile"
else:
tar = tarfile.open(filename, 'r')
for info in tar:
print info.name, info.size, time.ctime(info.mtime)
tar.close()
9–24. 归档文件转换.
参考前两个问题的解决方案, 写一个程序, 在 ZIP (.zip) 和TAR/gzip (.tgz/.tar.gz) 或 TAR/bzip2 (.tbz/.tar.bz2) 归档文件间移动文件. 文件可能是已经存在的, 必要时请创建文件.
import tarfile
import zipfile
import os
def movefile(src, dst, filename):
if src.endswith('.zip') and dst.endswith(('.tar.gz', '.tgz', '.tbz', '.tar.bz2')):
zipobj = zipfile.ZipFile(src, 'r')
content = zipobj.read(filename)
zipobj.close()
with open(filename, 'w') as f:
f.write(content)
tar = tarfile.open(dst, 'r')
ls = tar.getnames()
tar.extractall()
tar.close()
mode = 'w:gz' if dst.endswith(('tar.gz', '.tgz')) else 'w:bz2'
tar = tarfile.open(dst, mode)
for name in ls+[filename]:
tar.add(name)
os.remove(name)
tar.close()
elif src.endswith(('.tar.gz', '.tgz', '.tbz', '.tar.bz2')) and dst.endswith('.zip'):
tar = tarfile.open(src, 'r')
tar.extract(filename)
tar.close()
zipobj = zipfile.ZipFile(dst, 'a')
zipobj.write(filename)
zipobj.close()
os.remove(filename)
if __name__ == '__main__':
os.chdir(r'.\wx')
movefile('Desktop.zip', 'sample.tar.bz2', 'answer.txt')
movefile('sample.tar.gz', 'Desktop.zip', 'wx.txt')
9–25. 通用解压程序.
创建一个程序, 接受任意数目的归档文件以及一个目标目录做为参数.归档文件格式可以是 .zip, .tgz, .tar.gz, .gz, .bz2, .tar.bz2, .tbz 中的一种或几种. 程序会把第一个归档文件解压后放入目标目录, 把其它归档文件解压后放入以对应文件名命名的目录下(不包括扩展名). 例如输入的文件名为 header.txt.gz 和 data.tgz , 目录为 incoming ,header.txt 会被解压到 incoming 而 data.tgz 中的文件会被放入 incoming/data .
import tarfile
import zipfile
import os
def extract(path, filename):
if filename.endswith('.zip'):
with zipfile.ZipFile(filename, 'r') as f:
f.extractall(path)
elif filename.endswith(('.tgz', '.tar.gz', '.bz2', '.tbz', 'tar')):
with tarfile.open(filename, 'r') as f:
f.extractall(path)
def decompress(target, *files):
if not os.path.exists(target):
os.mkdir(target)
extract(target, files[0])
for name in files[1:]:
dirname = os.path.splitext(os.path.basename(name))[0]
dirname = '.\\' + target + '\\' + dirname
os.mkdir(dirname)
extract(dirname, name)
if __name__ == '__main__':
os.chdir(r'.\wx')
decompress('test', 'sample.tar', 'Desktop.zip', 'sample1.tar.bz2', 'sample2.tar.gz')