1. 程式人生 > >《Python核心程式設計》第9章 習題

《Python核心程式設計》第9章 習題

9–1. 檔案過濾. 顯示一個檔案的所有行, 忽略以井號( # )開頭的行. 這個字元被用做Python , Perl, Tcl, 等大多指令碼檔案的註釋符號.附加題: 處理不是第一個字元開頭的註釋.

f=open('test.txt','r')
for eachline in f:
    if eachline.startswith('#'):
        continue
    elif '#' in eachline:
        loc=eachline.find('#')
        print eachline[:loc]
    else:
        print eachline,
f.close()
9–2. 檔案訪問. 提示輸入數字 N 和檔案 F, 然後顯示檔案 F 的前 N 行.
N=int(raw_input('pls input a number:'))
F=raw_input('pls input a file name:')
f=open(F,'r')
alllines=f.readlines()
f.close()
for i in range(N):
    print alllines[i],
9–3. 檔案資訊. 提示輸入一個檔名, 然後顯示這個文字檔案的總行數.
F=raw_input('pls input a file name:')
f=open(F,'r')
alllines=f.readlines()
f.close()
print len(alllines)
9–4. 檔案訪問. 

寫一個逐頁顯示文字檔案的程式. 提示輸入一個檔名, 每次顯示文字檔案的 25 行, 暫停並向用戶提示"按任意鍵繼續.", 按鍵後繼續執行.

import os
F=raw_input('pls input a file name:')
n=0
f=open(F,'r')
for i in f:
    print i,
    n+=1
    if n==25:
        n=0
        os.system('pause')
f.close()
9-5 考試成績,改進你的考試成績問題(練習5-3和6-4),要求能從多個檔案中讀入考試成績。檔案的資料格式由你自己決定。
f=open('test.txt','r')
scores=[]
for i in f:
    if 0<=int(i.strip())<=100:
        scores.append(int(i.strip()))
    if int(i.strip())<60:
         print 'score is F' ,i
    elif 60<=int(i.strip())<=69:
         print 'score is D',i
    elif 70<=int(i.strip())<=79:
         print 'score is C',i
    elif 80<=int(i.strip())<=89:
         print 'score is B',i
    elif 90<=int(i.strip())<=100:
         print 'score is A',i
    else:
         print 'score wrong,please input again',i
f.close()
print 'average score is %f' %(sum(scores)//len(scores))
9–6. 檔案比較. 寫一個比較兩個文字檔案的程式. 如果不同, 給出第一個不同處的行號和列號.
F1=raw_input('pls input a file name:')
F2=raw_input('pls input a file name:')
f1=open(F1,'r')
f1alllines=f1.readlines()
f1.close()
f2=open(F2,'r')
f2alllines=f2.readlines()
f2.close()
len1=len(f1alllines)
len2=len(f2alllines)
smallfile=len1 if len1<=len2 else len2
for i in range(smallfile):
    if cmp(f1alllines[i],f2alllines[i])!=0:
        print 'row is %d ' %(i+1)
        len3=len(f1alllines[i])
        len4=len(f2alllines[i])
        smallstr=len3 if len3<=len4 else len4
        for j in range(smallstr):
            if cmp(f1alllines[i][j],f2alllines[i][j])!=0:
                print 'column is %d ' %(j+1)
                break
        break
else:
    if len1==len2:
        print '2 files equal'
    else:
        print 'row is %d ' %(i+2)
9–7. 解析檔案. Win32 使用者: 建立一個用來解析 Windows .ini 檔案的程式. POSIX 使用者:建立一個解析 /etc/serves 檔案的程式. 其它平臺使用者: 寫一個解析特定結構的系統配置檔案的程式.
option={}
f=open(r'c:\windows\win.ini')
for line in f:  
    if line.startswith(';'):  
        continue  
    if line.startswith('['):
        iterm=[]
        name = line[1:line.rfind(']')]  
        option.setdefault(name,iterm)
        continue  
    if '=' in line:  
        option[name].append(line.strip()) 
print option  
9–8. 模組研究. 提取模組的屬性資料. 提示使用者輸入一個模組名(或者從命令列接受輸入).然後使用 dir() 和其它內建函式提取模組的屬性, 顯示它們的名字, 型別, 值.
m=raw_input('pls input a module name: ')
module=__import__(m)
ml=dir(module)
print ml
for i in ml:
    print 'name: ',i
    print 'type: ',type(getattr(module,i))
    print 'value: ',getattr(module,i)
    print
9–9. Python 文件字串. 

進入 Python 標準庫所在的目錄. 檢查每個 .py 檔案看是否有__doc__ 字串, 如果有, 對其格式進行適當的整理歸類. 你的程式執行完畢後, 應該會生成一個漂亮的清單. 裡邊列出哪些模組有文件字串, 以及文件字串的內容. 清單最後附上那些沒有文件字串模組的名字.附加題: 提取標準庫中各模組內全部類(class)和函式的文件.

import os
pymodules={}
path=r'D:\Program Files\Python27\Lib'
pyfiles=[f for f in os.listdir(path) if f.endswith('.py')]
for f in pyfiles:
    module=f[:-3]
    pymodules.setdefault(module,'')
    pyfile=path+os.sep+f
    fobj=open(pyfile)
    doc=False
    for line in fobj:
        if line.strip().startswith('"""') and line.strip().endswith('"""'):
            pymodules[module]+=line
            fobj.close()
            break
        elif (line.strip().startswith('"""') or line.strip().startswith('r"""')) and len(line)>3:
            doc=True
            pymodules[module]+=line
            continue
        elif doc:
            if line=='"""':
                pymodules[module]+=line
                fobj.close()
                doc=False
                break
            else:
                pymodules[module]+=line
        else:
            continue
    else:
        fobj.close()
         
hasdoc=[]
nodoc=[]
for module in pymodules:
    if pymodules[module]:
        hasdoc.append(module)
    else:
        nodoc.append(module)

print 'module has no doc:'
for key in nodoc:
    print key,
print '\n'
print 'module has doc:'
for key in hasdoc:
    print 'module:',key
    print 'doc:',pymodules[key]
9-10 不會

9–11. Web 站點地址.
a) 編寫一個 URL 書籤管理程式. 使用基於文字的選單, 使用者可以新增, 修改或者刪除書籤資料項. 書籤資料項中包含站點的名稱, URL 地址, 以及一行簡單說明(可選). 另外提供檢索功能,可以根據檢索關鍵字在站點名稱和 URL 兩部分查詢可能的匹配. 程式退出時把資料儲存到一個磁碟檔案中去; 再次執行時候載入儲存的資料.
b)改進 a) 的解決方案, 把書籤輸出到一個合法且語法正確的 HTML 檔案(.html 或 htm )中,這樣使用者就可以使用瀏覽器檢視自己的書籤清單. 另外提供建立"資料夾"功能, 對相關的書籤進行分組管理.
附加題: 請閱讀 Python 的 re 模組瞭解有關正則表示式的資料, 使用正則表示式對使用者輸入的 URL 進行驗證.

import re,os

def checkurl(url):    
    regex = re.compile(
            r'^(?:http|ftp)s?://' # http:// or https://
            r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain...
            r'localhost|' #localhost...
            r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
            r'(?::\d+)?' # optional port
            r'(?:/?|[/?]\S+)$', re.IGNORECASE)
    if regex.match(url):
        return True
    else:
        return False

def geturl():
    name=raw_input('pls input a url name:')    
    while 1:
        url=raw_input('pls input a url address:')
        if checkurl(url):
            break
        else:
            print 'wrong url format,pls input again'
    mark=raw_input('pls input a url mark:')
    folder=raw_input('pls input a url folder:')
    return (name,url,mark,folder)

def load(filename):
    f=open(filename,'a+')
    bmlist=f.readlines()
    f.close()
    return bmlist

def save(bmlist,filename):
    f=open(filename,'w+')
    for line in bmlist:
        if len(line)==0:
            continue
        f.write(line)
    f.close()

def add(bmlist,name,url,mark,folder='default'):
    bookmark=''
    bookmark=name+';'+url+';'+mark+';'+folder+os.linesep
    if bookmark not in bmlist:
        bmlist.append(bookmark)

def modify(bmlist,index,name,url,mark,folder):
    bookmark=''
    bookmark=name+';'+url+';'+mark+';'+folder+os.linesep
    bmlist[index]=bk

def delbm(bmlist,index):
    bmlist.pop(index)

def findbk(bmlist,fname,furl):
    for i,item in enumerate(bmlist):
        (name,url,mark,folder)=item.split(';')
        if fname and furl:
            if (fname in name) and (furl in url):
                return i
        if fname and (fname in name):
            return i
        if furl and (furl in url):
            return i
    else:
        return -1

def output2html(bmlist):   
    for i,item in enumerate(bmlist):
        (name, url, mark, folder) = item.split(';')
        os.mkdir(folder.strip())
        filename=name.strip()+'.html'
        f=open(filename,'w+')
        fmt = '%d\t%s\t<a href=%s>%s</a>\t%s\t%s<br>'
        f.write('<html><head><title>bookmark</title></head><body>') 
        content = fmt % (i+1, name, r'http:\\' + url, url, mark, folder)   
        f.write(content)  
        f.write('</body></html>')
        f.close()
        os.rename(filename,folder.strip()+os.sep+filename)

bmlist=load(r'url.txt')
print bmlist
while True:
    print '0. quit'
    print '1. add a url bookmark'
    print '2. modify a url bookmark'
    print '3. delete a url bookmark'
    print '4. find a url bookmark'
    print '5. output url bookmark as html'
    print '\n'

    iInput = input("please input operation num: ")
    if (0 == iInput):
        save(bmlist,r'url.txt')
        break
    elif (iInput<0 or iInput>5):
        print 'Error input operation, try agin. 0 operation is quit\n'
        continue
    elif 1 == iInput:
        data=geturl()
        add(bmlist,*data)
        print bmlist
    elif 2 == iInput:
        index=int(raw_input('bookmark index:'))
        data=geturl()
        modify(bmlist,index,*data)
        print bmlist
    elif 3 == iInput:
        index=int(raw_input('bookmark index:'))
        delbm(bmlist,index)
        print bmlist
    elif 4 == iInput:
        name=raw_input('url name:')
        url=raw_input('url address:')
        index=findbk(bmlist,name,url)
        if index==-1:
            print 'not found'
        else:
            print bmlist[index]
    elif 5 == iInput:
        output2html(bmlist)
9-12 使用者名稱和密碼。回顧練習7-5,修改程式碼使之可以支援“上次登入時間”。請參閱time模組中的文件瞭解如何記錄使用者上次登入的時間。另外提供一個系統管理員,他可以匯出所有使用者的使用者名稱,密碼(如需要可以加密),以及上次登入時間。

a)資料應儲存在磁碟中,使用冒號:分隔,一次寫入一行,例如“Joe:boohoo:953176591.145,檔案中資料的行數應該等於你係統上的使用者數。

b)進一步改進你的程式,不再一次寫入一行,而使用pickle模組儲存整個資料物件。請參閱pickle模組的文件瞭解如何序列化/扁平化物件,以及如何讀寫儲存的物件。一般來說,這個解決方案的程式碼行數要比a)少;

c)使用shelve模組替換pickle模組,由於可以省去一些維護程式碼,這個解決方案的程式碼比b)的更少。

from datetime import datetime
import hashlib,os
import pickle as p
import shelve as s
 
db={}
def newuser():
    value=[]
    prompt='login name desired again: '
    while True:
        name=raw_input(prompt).lower()
        if not name.isalnum() and '' in name:
            print 'name format error'
            continue
        else:
            if db.has_key(name):
                prompt='name taken,try another: '
                continue
            else:
                break
    pwd=raw_input('login passwd desired: ')
    m=hashlib.md5()
    m.update(pwd)
    value.append(m.hexdigest())
    value.append(datetime.now())
    db[name]=value
    print 'new user is %s, register time is %s' %(name,db[name][1])

def olduser():
    name=raw_input('login name desired again: ').lower()
    pwd=raw_input('login passwd desired: ')
    m=hashlib.md5()
    m.update(pwd)
    passwd=db.get(name)
    if passwd[0]==m.hexdigest():
        newtime=datetime.now()
        if (newtime-db[name][1]).days==0 and (newtime-db[name][1]).seconds<14400:
            print 'you already logged in at %s: ' %(db[name][1])
        else:
            passwd[1]=newtime
            print 'welcome back %s, login time is %s' %(name,passwd[1])
        
    else:
        print 'login incorrect'

def removeuser():
    print db
    name=raw_input('input a user name to remove: ').lower()
    if name in db:
        db.pop(name)
    else:
        print 'input error'

def userlogin():
    while True:
        name=raw_input('login name desired: ').lower()
        if not name.isalnum() and '' in name:
            print 'name format error'
            continue
        else:
            if not db.has_key(name):
                print 'user name is not in db'
                answer=raw_input('register a new user? y/n').lower()
                if 'y'==answer:
                    newuser()
                    break
                elif 'n'==answer:
                    break
            else:
                print 'user name is already in db'
                olduser()
                break

def outputA():
    print db
    f=open('account.txt','w')
    for key in db:
        user=key+':'+db[key][0]+':'+str(db[key][1])+os.linesep
        f.write(user)
    f.close()

def outputB():
    accountfile='pickle.data'
    f=open(accountfile,'w')
    p.dump(db,f)
    f.close()

    f=open(accountfile)
    accountdb=p.load(f)
    print accountdb

def outputC():
    accountfile='shelve.data'
    accountdb=s.open(accountfile,'c')
    accountdb['data']=db
    accountdb.close()

    accountdb=s.open(accountfile,'r')
    print accountdb['data']

def adminlogin():
    while True:
        name=raw_input('login name desired: ').lower()
        if not name.isalnum() and '' in name:
            print 'name format error'
            continue
        else:
            pwd=raw_input('login passwd desired: ')
            if name=='root' and pwd=='root':
                print 'welcom admin'
                break
            else:
                print 'user name or passwd is wrong,input again'
    if len(db)==0:
        print 'there is nothing you can do'
    else:
        answer=raw_input('output all account? y/n').lower()
        if 'y'==answer:
            #outputA()
            #outputB()
            outputC()
        elif 'n'==answer:
            print 'bye'   

def showmenu():
    prompt="""
    (A)dmin Login
    (U)ser Login
    (R)emove a existing user
    (Q)uit
     Enter choice:"""

    done=False
    while not done:
        chosen=False
        while not chosen:
            try:
                choice=raw_input(prompt).strip()[0].lower()
            except (EOFError,keyboardInterrupt):
                choice='q'
            print '\nYou picked: [%s]' % choice
            if choice not in 'aurq':
                print 'invalid option,try again'
            else:
                chosen=True

        if choice=='q':
            done=True
        if choice=='r':
            removeuser()
        if choice=='u':
            userlogin()
        if choice=='a':
            adminlogin()

if __name__=='__main__':
    showmenu()
9–13. 命令列引數
a) 什麼是命令列引數, 它們有什麼用?

命令列引數是呼叫某個程式時除程式名以外的其他引數。命令列引數使程式設計師可以在啟動一個程式時對程式行為作出選擇。
b) 寫一個程式, 打印出所有的命令列引數.

import sys
print str(sys.argv)
9-14 記錄結果。修改你的計算器程式(練習5-6)使之接受命令列引數。例如$ calc.py 1 + 2 只輸出計算結果。另外,把每個表示式和它的結果寫入到一個磁碟檔案中,當使用下面的命令時 $ calc.py print 會把記錄的內容顯示到螢幕上,然後重置檔案。這裡是樣例展示:

$ calc.py 1 + 2

3

$ calc.py 3 ^ 3

27

$ calc.py print

1 + 2

3

3 ^ 3

27

$ calc.py print

import sys,os

if sys.argv[1]=='print':
    if os.path.exists(r'test.txt'):
        f=open(r'test.txt','r')
        for line in f:
            print line
        f.close()
    else:
        print 'no file yet'
    f=open(r'test.txt','w')
    f.close()
else:
    print sys.argv[1],sys.argv[2],sys.argv[3]
    a,b=sys.argv[1],sys.argv[3]
    operation=sys.argv[2]
    expression=sys.argv[1]+''+sys.argv[2]+''+sys.argv[3]+os.linesep
    f=open(r'test.txt','a+')
    f.write(expression)
    if '+' == operation:
        print float(a)+float(b)
        result=str(float(a)+float(b))+os.linesep+os.linesep
        f.write(result)
    elif '-' == operation:
        print float(a)-float(b)
        result=str(float(a)-float(b))+os.linesep+os.linesep
        f.write(result)
    elif '**' == operation:
        print float(a)**float(b)
        result=str(float(a)**float(b))+os.linesep+os.linesep
        f.write(result)
    elif '/' == operation:
        print float(a)/float(b)
        result=str(float(a)/float(b))+os.linesep+os.linesep
        f.write(result)
    elif '%' == operation:
        print float(a)%float(b)
        result=str(float(a)%float(b))+os.linesep+os.linesep
        f.write(result)
    elif '*' == operation:
        print float(a)*float(b)
        result=str(float(a)*float(b))+os.linesep+os.linesep
        f.write(result)
    f.close()
9–15. 複製檔案. 提示輸入兩個檔名(或者使用命令列引數). 把第一個檔案的內容複製到第二個檔案中去.
import os
file1=raw_input('input first file name:')
file2=raw_input('input second file name:')

f1=open(file1,'r')
f2=open(file2,'a+')
f2.write(os.linesep)
for line in f1:
    f2.write(line)
f1.close()
f2.close()
9–16. 文字處理. 

人們輸入的文字常常超過螢幕的最大寬度. 編寫一個程式, 在一個文字檔案中查詢長度大於 80 個字元的文字行. 從最接近 80 個字元的單詞斷行, 把剩餘檔案插入到下一行處.程式執行完畢後, 應該沒有超過 80 個字元的文字行了.

import os

content=[]
f=open(r'test.txt','r')
lines=f.readlines()
f.close()

for line in lines:
    if len(line)<=80:
        content.append(line)
    else:
        words=line.strip().split()
        sum=0
        l=''
        for w in words:
            w+=' '
            sum+=len(w)
            if sum<80:
                l+=w
            else:
                content.append(l)
                l=w
                sum=len(w)
        else:
            content.append(l)
            l=''

f=open(r'test1.txt','w')
for item in content:
    f.write(item+os.linesep)
f.close()
9–17. 文字處理. 

建立一個原始的文字檔案編輯器. 你的程式應該是選單驅動的, 有如下這些選項:
1) 建立檔案(提示輸入檔名和任意行的文字輸入),
2) 顯示檔案(把檔案的內容顯示到螢幕),
3) 編輯檔案(提示輸入要修改的行, 然後讓使用者進行修改),
4) 儲存檔案, 以及
5) 退出.

import os

def create(filename):
    content=[]
    while True:
        line=raw_input('pls input a line,quit as e:')
        if line != 'e':
            content.append(line)
        else:
            break
    f=open(filename,'w')
    for line in content:
        f.write(line+os.linesep)
    f.close()

def show(filename):
    if os.path.exists(filename):
        f=open(filename)
        for line in f:
            print line,
        f.close()
    else:
        print 'no file yet'

def edit(filename,index,content):
    f=open(filename)
    ls=f.readlines()
    f.close()
    ls[index]=content
    return ls

def save(filename,ls):
    f=open(filename,'w')
    for line in ls:
        f.write(line)
    f.close()

def main():
    filename=''
    ls=[]
    while True:
        print '\n'
        print '1. create a file'
        print '2. show a file'
        print '3. edit a file'
        print '4. save a file'
        print '5. quit'

        ch=raw_input('input a choice:')
        if ch not in '1234':
            break
        elif ch=='1':
            filename=raw_input('input a file name:')
            create(filename)
        elif ch=='2':
            filename=raw_input('input a file name:')
            show(filename)
        elif ch=='3':
            if filename == '':  
                filename = raw_input('file name: ')
            index=int(raw_input('input a index of line:'))
            content=raw_input('pls input a line:')
            ls=edit(filename,index,content)
        elif ch=='4':
            save(filename,ls)
            
if __name__=='__main__':
    main()
9–18. 搜尋檔案. 提示輸入一個位元組值(0 - 255)和一個檔名. 顯示該字元在檔案中出現的次數.
value=int(raw_input('input a value between 0 and 255:'))
filename=raw_input('input a fielname:')

ch=chr(value)
f=open(filename)
print sum(iterm.count(ch) for iterm in f)
f.close()
9–19. 建立檔案. 

建立前一個問題的輔助程式. 建立一個隨機位元組的二進位制資料檔案, 但某一特定位元組會在檔案中出現指定的次數. 該程式接受三個引數:
1) 一個位元組值( 0 - 255 ),
2) 該字元在資料檔案中出現的次數, 以及
3) 資料檔案的總位元組長度.
你的工作就是生成這個檔案, 把給定的位元組隨機散佈在檔案裡, 並且要求保證給定字元在檔案中只出現指定的次數, 檔案應精確地達到要求的長度.

import random
def createfile(value,count,len):
    ls=[]
    n=len-count
    for i in range(n):
        ran=random.randint(0,255)
        if ran!=value:
            ls.append(chr(ran))
        elif ran==value and value==0:
            ran=random.randint(1,255)
            ls.append(chr(ran))
        elif ran==value and value==255:
            ran=random.randint(0,254)
            ls.append(chr(ran))
        elif ran==value:
            ran=random.randint(0,value-1)
            ls.append(chr(ran))
    for i in range(count):
        ls.insert(random.randint(0,n),chr(value))
    f=open(r'test.txt','wb')
    f.write(''.join(ls))
    f.close()
createfile(97,3,50)
f=open(r'test.txt','rb')
for i in f:
    print i
f.seek(0,0)
print len(f.readlines()[0])
f.close()
9–20. 壓縮檔案. 

寫一小段程式碼, 壓縮/解壓縮 gzip 或 bzip 格式的檔案. 可以使用命令列下的 gzip 或 bzip2 以及 GUI 程式 PowerArchiver , StuffIt , 或 WinZip 來確認你的 Python支援這兩個庫.

import gzip
#compress
f_in=open(r'test.txt','rb')
f_out=gzip.open(r'test.txt.gz','wb')
f_out.writelines(f_in)
f_out.close()
f_in.close()
#decompress
f=gzip.open(r'test.txt.gz','rb')
f_out=open(r'test1.txt','wb')
content=f.read()
f_out.write(content)
f.close()
f_out.close()
9–21. ZIP 歸檔檔案. 

建立一個程式, 可以往 ZIP 歸檔檔案加入檔案, 或從中提取檔案,有可能的話, 加入建立ZIP 歸檔檔案的功能.

import zipfile
def create_zipfile(zipname,filename1,filename2):
    z=zipfile.ZipFile(zipname,'w')
    z.write(filename1)
    z.write(filename2)
    z.close()

def add_zipfile(zipname,filename):
    z=zipfile.ZipFile(zipname,'a')
    z.write(filename)
    z.close()

def extract_zipfile(zipname,filename):
    z=zipfile.ZipFile(zipname,'r')
    z.extract(filename)
    z.close()

if __name__=='__main__':
    create_zipfile(r'test.zip',r'test.txt',r'test1.txt')
    add_zipfile(r'test.zip',r'test2.txt')
    extract_zipfile(r'test.zip',r'test.txt')
9–22. ZIP 歸檔檔案. 

unzip -l 命令顯示出的 ZIP 歸檔檔案很無趣. 建立一個 Python指令碼 lszip.py , 使它可以顯示額外資訊: 壓縮檔案大小, 每個檔案的壓縮比率(通過比較壓縮前後文件大小), 以及完成的 time.ctime() 時間戳, 而不是隻有日期和 HH:MM .
提示: 歸檔檔案的 date_time 屬性並不完整, 無法提供給 time.mktime() 使用....這由你自己決定.

import zipfile,os,time

filename=raw_input('zip file name:')
print 'zip file size: %d bytes' %(os.stat(filename).st_size)
z=zipfile.ZipFile(filename,'r')
print 'filename\tdatetime\tsize\tcompress size\trate'
for info in z.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)
z.close()
9–23. TAR 歸檔檔案. 

為 TAR 歸檔檔案建立類似上個問題的程式. 這兩種檔案的不同之處在於 ZIP 檔案通常是壓縮的, 而 TAR 檔案不是, 只是在 gzip 和 bzip2 的支援下才能完成壓縮工作. 加入任意一種壓縮格式支援.附加題: 同時支援 gzip 和 bzip2 .

import tarfile
def create_tarfile(tarname,filename1,filename2):
    t=tarfile.open(tarname,'w:gz')#w:bz2
    t.add(filename1)
    t.add(filename2)
    t.close()

def extract_tarfile(tarname):
    t=tarfile.open(tarname,'r')
    t.extractall(r'D:\test')
    t.close()

if __name__=='__main__':
    create_tarfile(r'test.tar.gz',r'test.txt',r'test1.txt')
    extract_tarfile(r'test.tar.gz')
9–24. 歸檔檔案轉換. 

參考前兩個問題的解決方案, 寫一個程式, 在 ZIP (.zip) 和TAR/gzip (.tgz/.tar.gz) 或 TAR/bzip2 (.tbz/.tar.bz2) 歸檔檔案間移動檔案. 檔案可能是已經存在的, 必要時請建立檔案.

import zipfile,tarfile,os

def movefile(src,dst,filename):
    if src.endswith('.zip') and dst.endswith(('.tar.gz', '.tgz', '.tbz', '.tar.bz2')):
        z=zipfile.ZipFile(src,'a')
        if filename not in z.namelist():
            f=open(filename,'w')
            f.close()
            z.write(filename)
            z.extract(filename)
        else:
            z.extract(filename)
        z.close()
        t=tarfile.open(dst,'r')
        ls=t.getnames()
        if filename not in ls:
            t.extractall()
            t.close()
            mode='w:gz' if dst.endswith(('tar.gz','tgz')) else 'w:bz2'
            t=tarfile.open(dst,mode)
            for name in ls+[filename]:
                t.add(name)
                os.remove(name)
            t.close()
        t.close()
    elif src.endswith(('.tar.gz', '.tgz', '.tbz', '.tar.bz2')) and dst.endswith(('.zip')):
        t=tarfile.open(src,'r')
        if filename not in t.getnames():
            f=open(filename,'w')
            f.close()
        else:
            t.extract(filename)
        t.close()
        z=zipfile.ZipFile(dst,'a')
        if filename not in z.namelist():
            z.write(filename)
        z.close()
        os.remove(filename)

if __name__=='__main__':
    movefile(r'test.zip',r'test.tar.gz',r'test2.txt')
    movefile(r'test.tar.gz',r'test.zip',r'test2.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 zipfile,tarfile,gzip,bz2,os

def depressfile(src,dst):
    if os.path.isdir(src):
        filenames=os.listdir(src)
        if filenames[0].endswith(('.tar.gz', '.tgz', '.tbz', '.tar.bz2')):
            t=tarfile.open(src+os.sep+filenames[0],'r')
            t.extractall(dst)
            t.close()
        elif filenames[0].endswith('.gz'):
            g=gzip.open(src+os.sep+filenames[0],'rb')
            ug=open(dst+os.sep+filenames[0][:-3],'wb')
            data=g.read()
            ug.write(data)
            ug.close()
            g.close()
        elif filenames[0].endswith('.bz2'):
            b=bz2.BZ2File(src+os.sep+filenames[0],'r')
            ub=open(dst+os.sep+filenames[0][:-4],'w')
            data=b.read()
            ub.write(data)
            ub.close()
            b.close()
        elif filenames[0].endswith('.zip'):
            z=zipfile.ZipFile(src+os.sep+filenames[0],'r')
            z.extractall(dst)
            z.close()
        filenames.remove(filenames[0])
        for name in filenames:
            dirname = os.path.splitext(os.path.basename(name))[0]
            if dirname in os.listdir(dst):
                dirname = dst+os.sep+dirname+str(filenames.index(name))
            else:
                dirname = dst+os.sep+dirname
            os.mkdir(dirname)
            if name.endswith(('.tar.gz', '.tgz', '.tbz', '.tar.bz2')):
                t=tarfile.open(src+os.sep+name,'r')
                t.extractall(dirname)
                t.close()
            elif name.endswith('.gz'):
                g=gzip.open(src+os.sep+name,'rb')
                ug=open(dirname+os.sep+name[:-3],'wb')
                data=g.read()
                ug.write(data)
                ug.close()
                g.close()
            elif name.endswith('.bz2'):
                b=bz2.BZ2File(src+os.sep+name,'r')
                ub=open(dirname+os.sep+name[:-4],'w')
                data=b.read()
                ub.write(data)
                ub.close()
                b.close()
            elif name.endswith('.zip'):
                z=zipfile.ZipFile(src+os.sep+name,'r')
                z.extractall(dirname)
                z.close()
    else:
        print '%s is not a directory,input again' %(src)

if __name__=='__main__':
    depressfile(r'D:\1',r'D:\2')