《Python核心编程》第9章 文件和输入输出 练习

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')

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值