本文將詳細(xì)解釋這些函數(shù)的使用方法。首先,我們介紹Python語言中類似于Windows系統(tǒng)的dir命令的列出文件功能,然后描述如何測(cè)試一個(gè)文件名
對(duì)應(yīng)的是一個(gè)標(biāo)準(zhǔn)文件、目錄還是鏈接,以及提取文件大小和日期的方法。之后,我們還將介紹如何刪除文件和目錄,如何復(fù)制和刪除文件,以及怎樣將一個(gè)完整的
文件路徑分解成目錄部分和文件名部分,最后,我們講解目錄的創(chuàng)建,以及如何在目錄樹中移動(dòng)目錄并處理文件。 一、顯示目錄內(nèi)容 當(dāng)我們想要列出當(dāng)前目錄中所有擴(kuò)展名為.jpg或.gif的文件的時(shí)候,就可以使用glob模塊來完成此項(xiàng)任務(wù),如下所示: import glob filelist = glob.glob('*.jpg') + glob.glob('*.gif') 上述代碼使用了glob函數(shù),該函數(shù)的參數(shù)為要顯示的文件類型。在這里,文件類型是通過類似UNIX操作系統(tǒng)shell風(fēng)格通配符描述的一些文件名來指定的。這些通配符的使用方法,具體請(qǐng)參考fnmatch模塊的文檔,那里有具體的說明和示例。 為了顯示一個(gè)目錄中的全部文件,可以使用如下所示的os.listdir函數(shù):
復(fù)制代碼 代碼如下: files = os.listdir(r'C:\hpl\scripting\src\py\intro') #適用于 Windows files = os.listdir('/home/hpl/scripting/src/py/intro') # 適用于Unix # 跨平臺(tái)版本: files = os.listdir(os.path.join(os.environ['scripting'], 'src', 'py', 'intro')) files = os.listdir(os.curdir) # 當(dāng)前目錄中的所有文件 files = glob.glob('*') + glob.glob('.*')
二、測(cè)試文件類型
我們知道,文件名、目錄名和鏈接名都是用一個(gè)字符串作為其標(biāo)識(shí)符的,但是給我們一個(gè)標(biāo)識(shí)符,我們?cè)撊绾未_定它所指的到底是常規(guī)文件文件名、目錄名還是鏈
接名呢?這時(shí),我們可以使用os.path模塊提供的isfile函數(shù)、isdir函數(shù)和islink函數(shù)來達(dá)成我們的目標(biāo),如下所示: 復(fù)制代碼 代碼如下: print myfile, '是一個(gè)', if os.path.isfile(myfile): print 'plain file' if os.path.isdir(myfile): print 'directory' if os.path.islink(myfile): print 'link'
您還可以查找文件的日期及其大?。?
復(fù)制代碼 代碼如下: time_of_last_access = os.path.getatime(myfile) time_of_last_modification = os.path.getmtime(myfile) size = os.path.getsize(myfile)
這里的時(shí)間以秒為單位,并且從1970年1月1日開始算起。為了獲取以天為單位的最后訪問日期,可以使用下列代碼: import time # time.time()返回當(dāng)前時(shí)間 age_in_days = (time.time()-time_of_last_access)/(60*60*24) 為了獲取文件的詳細(xì)信息,可以使用os.stat函數(shù)和stat模塊中的其它實(shí)用程序來達(dá)到目的,如下:
復(fù)制代碼 代碼如下: import stat myfile_stat = os.stat(myfile) size = myfile_stat[stat.ST_SIZE] mode = myfile_stat[stat.ST_MODE] if stat.S_ISREG(mode): print '%(myfile)是一個(gè)常規(guī)文件,大小為 %(size)d 字節(jié)' %\ vars()
有關(guān)stat模塊的詳細(xì)信息,請(qǐng)參見Python Library Reference。若想測(cè)試一個(gè)文件的讀、寫以及執(zhí)行權(quán)限,可以用os.access函數(shù),具體如下所示: if os.access(myfile, os.W_OK): print myfile, '具有寫權(quán)限' if os.access(myfile, os.R_OK | os.W_OK | os.X_OK): print myfile, '具有讀、寫以及執(zhí)行權(quán)限' 像上面這樣的測(cè)試代碼,對(duì)CGI腳本來說非常有用。 三、文件和目錄的刪除
若要?jiǎng)h除單個(gè)文件的話,可以使用os.remove函數(shù),例如:os.remove('mydata.dat')。Os.remove的別名是
os.unlink,不過后者跟傳統(tǒng)的UNIX操作系統(tǒng)以及Perl中清除文件的函數(shù)重名。我們可以使用下列方式來刪除一組文件,如所有以.jpg以
及*.gif為擴(kuò)展名的文件: for file in glob.glob('*.jpg') + glob.glob('*.gif'): os.remove(file) 大家知道,只有當(dāng)目錄中內(nèi)容已經(jīng)被清空的時(shí)候,我們才可以使用rmdir命令來刪除該目錄。不過,我們經(jīng)常想要?jiǎng)h除一個(gè)含有許多文件的目錄樹,這時(shí)我們可以使用shutil模塊提供的rmtree函數(shù),如下所示: shutil.rmtree('mydir') 它相當(dāng)于UNIX操作系統(tǒng)中的命令rm -rf mydir。 我們可以建立一個(gè)自定義函數(shù),使其在進(jìn)行刪除操作的時(shí)候?qū)⑽募湍夸涀鐾葘?duì)待,其典型用法如下所示: remove('my.dat') #刪除當(dāng)個(gè)文件my.dat remove('mytree') #刪除單個(gè)目錄樹 mytree # 通過字符串列表中的名稱來刪除多個(gè)文件/目錄樹: remove(glob.glob('*.tmp') + glob.glob('*.temp')) remove(['my.dat','mydir','yourdir'] + glob.glob('*.data')) 下面是remove函數(shù)的實(shí)現(xiàn): def remove(files): """刪除一個(gè)或多個(gè)文件和/或目錄。""" if isinstance(files, str): # files是個(gè)字符串嗎? files = [files] # 把files從字符串轉(zhuǎn)為列表 if not isinstance(files, list): # files不是列表嗎? for file in files: if os.path.isdir(file): shutil.rmtree(file) elif os.path.isfile(file): os.remove(file) 下面測(cè)試一下remove函數(shù)的靈活性:
復(fù)制代碼 代碼如下: # 建立10個(gè)目錄tmp_* ,以及10各文件tmp__*: for i in range(10): os.mkdir('tmp_'+str(i)) f = open('tmp__'+str(i), 'w'); f.close() remove('tmp_1') # tmp_1為目錄 remove(glob.glob('tmp_[0-9]') + glob.glob('tmp__[0-9]'))
作為上述remove函數(shù)實(shí)現(xiàn)的一個(gè)注記,我們進(jìn)行了下列測(cè)試: if not isinstance(files, list): 它實(shí)際上是過于嚴(yán)厲。我們需要的只是一個(gè)被遍歷的一個(gè)文件/目錄名序列。實(shí)際上,我們并不關(guān)心名稱是否存儲(chǔ)在一個(gè)列表、元組或者數(shù)值數(shù)組中,所以更好的測(cè)試應(yīng)該像下面這樣: if not operator.isSequenceType(files): 四、文件的復(fù)制與重命名 當(dāng)我們要復(fù)制文件的時(shí)候,可以使用shutil模塊: import shutil shutil.copy(myfile, tmpfile) #拷貝最后訪問時(shí)間和最后修改時(shí)間: shutil.copy2(myfile, tmpfile) # 拷貝一個(gè)目錄樹: shutil.copytree(root_of_tree, destination_dir, True) Copytree的第三個(gè)參數(shù)規(guī)定對(duì)符號(hào)鏈接的處理,其中True表示保留符號(hào)鏈接;而False則意味著使用文件的物理副本替代符號(hào)鏈接。
Python語言能夠很好地支持路徑名的跨平臺(tái)組成:Os.path.join能使用正確的分界符(在UNIX和Mac OS X操作系統(tǒng)中使用/,在
Windows 上使用\)來聯(lián)接目錄和文件名,變量os.curdir和os.pardir分別表示當(dāng)前工作目錄及其父目錄。
像下面的UNIX操作系統(tǒng)命令 cp http://www.jb51.net/f1.c . 可以使用Python語言提供一個(gè)跨平臺(tái)的實(shí)現(xiàn): shutil.copy(os.path.join(os.pardir,os.pardir,'f1.c'), os.curdir) Os模塊中的rename函數(shù)通常被用于重命名一個(gè)文件: os.rename(myfile, 'tmp.1') # 將myfile重命名為'tmp.1' 這個(gè)函數(shù)也可用來在相同的文件系統(tǒng)之內(nèi)移動(dòng)文件。這里,我們將myfile移動(dòng)到目錄d下面: os.rename(myfile, os.path.join(d, myfile)) 在跨文件系統(tǒng)移動(dòng)文件的時(shí)候,可以先使用shutil.copy2來復(fù)制文件,然后再刪除原來的副本即可,如下: shutil.copy2(myfile, os.path.join(d, myfile)) os.remove(myfile) 后面這種移動(dòng)文件的方法是最安全的。 五、分解路徑名 假設(shè)我們使用變量fname來存放一個(gè)包含完整路徑的文件名,例如: /usr/home/hpl/scripting/python/intro/hw.py 有時(shí)候,我們需要將這樣的文件路徑拆分為基本名稱hw.py和目錄名/usr/home/hpl/scripting/python/intro。在Python語言中,可以使用下列代碼達(dá)到目的: basename = os.path.basename(fname) dirname = os.path.dirname(fname) # 或 dirname, basename = os.path.split(fname) 擴(kuò)展名是通過os.path.splitext函數(shù)提取出來的, root, extension = os.path.splitext(fname) 這樣,fname中的擴(kuò)展名部分即.py被賦給變量extension,而其余部分則賦給了變量root。如果想得到不帶點(diǎn)號(hào)的擴(kuò)展名的話,只需使用os.path.splitext(fname)[1][1:]即可。 假設(shè)一個(gè)文件名為f,其擴(kuò)展名隨意,若想將其擴(kuò)展名改為ext,可以使用下面的代碼: newfile = os.path.splitext(f)[0] + ext 下面是一個(gè)具體的示例: >>> f = '/some/path/case2.data_source' >>> moviefile = os.path.basename(os.path.splitext(f)[0] + '.mpg') >>> moviefile 'case2.mpg' 六、目錄的創(chuàng)建和移動(dòng) Os模塊中的函數(shù)mkdir可以用來創(chuàng)建目錄,而chdir函數(shù)則可以移動(dòng)目錄,如下: origdir = os.getcwd() # 將當(dāng)前位置記下來 newdir = os.path.join(os.pardir, 'mynewdir') if not os.path.isdir(newdir): os.mkdir(newdir) # 或者os.mkdir(newdir,'0755') os.chdir(newdir) ... os.chdir(origdir) # 返回原目錄 os.chdir(os.environ['HOME']) # 移到主目錄
假設(shè)我們想要在自己的主目錄下創(chuàng)建一個(gè)新目錄py/src/test1,但是目前py、src和test1都不存在。如果使用mkdir命令來創(chuàng)建的
話,需要使用三次才能建好這個(gè)嵌套的目錄,但是使用Python語言提供的os.makedirs命令的話,則無需這樣麻煩了,該命令可以一次建好整個(gè)目
錄: os.makedirs(os.path.join(os.environ['HOME'],'py','src','test1')) 七、遍歷目錄樹 下面的函數(shù)調(diào)用 os.path.walk(root, myfunc, arg)
將遍歷root目錄樹;然后,對(duì)每個(gè)目錄名dirname分別調(diào)用myfunc(arg, dirname,
files)即可,這里參數(shù)files是dir中的文件名列表(可通過調(diào)用os.listdir(dirname)來獲得);arg是用戶從調(diào)用代碼中傳
遞來的參數(shù)。對(duì)于UNIX操作系統(tǒng)用戶來說,Python語言中跨平臺(tái)的os.path.walk相當(dāng)于Unix命令find。 在解釋os.path.walk的用法的時(shí)候,人們常使用寫出主目錄中所有子目錄內(nèi)的文件的名稱為例進(jìn)行說明。當(dāng)然,我們也可以在一個(gè)交互式的Python命令行中使用下列代碼段來體會(huì)os.path.walk的使用: def ls(arg, dirname, files): print dirname, 'has the files', files os.path.walk(os.environ['HOME'], ls, None) 本例中,參數(shù)arg并非必需,所以在os.path.walk調(diào)用中讓其取值為None即可。 為了列出主目錄中所有大于1Mb的文件,可以使用下面的代碼: def checksize1(arg, dirname, files): for file in files: filepath = os.path.join(dirname, file) if os.path.isfile(filepath): size = os.path.getsize(filepath) if size > 1000000: size_in_Mb = size/1000000.0 arg.append((size_in_Mb, filename)) bigfiles = [] root = os.environ['HOME'] os.path.walk(root, checksize1, bigfiles) for size, name in bigfiles: print name, '大小為', size, 'Mb' 現(xiàn)在,我們使用arg來建立一個(gè)數(shù)據(jù)結(jié)構(gòu),這里是一個(gè)2元組構(gòu)成的列表,其中每個(gè)2元組存放文件的尺寸(以MB為單位)和完整的文件路徑。如果用于所有目錄的函數(shù)調(diào)用中都要更改arg的話,那么arg必須是一個(gè)可變的數(shù)據(jù)結(jié)構(gòu),即允許適當(dāng)?shù)剡M(jìn)行修改。
參數(shù)dirname是當(dāng)前正在訪問的目錄的絕對(duì)路徑,而參數(shù)files內(nèi)的文件名則是相對(duì)于dirname的相對(duì)路徑。在此期間,當(dāng)前工作目錄并沒有改
變,那就是說該腳本仍然呆在腳本啟動(dòng)時(shí)刻所在的目錄中。這就是為什么我們需要把filepath弄成帶有dirname和file的絕對(duì)路徑的原因。若要
改變當(dāng)前工作目錄為dirname,只要在針對(duì)每個(gè)目錄調(diào)用os.path.walk的函數(shù)中調(diào)用一下os.chdir(dirname),然后在該函數(shù)
的末尾重新調(diào)用os.chdir(dirname)將當(dāng)前工作目錄改回原值即可,如下所示: def somefunc(arg, dirname, files): origdir = os.getcwd(); os.chdir(dirname) os.chdir(origdir) os.path.walk(root, somefunc, arg) 當(dāng)然,如果您愿意也可以編寫具有類似功能的代碼來替代os.path.walk。下面的代碼,將針對(duì)每個(gè)文件而非每個(gè)目錄來調(diào)用的自定義函數(shù),如下所示: def find(func, rootdir, arg=None): # 對(duì)rootdir目錄中的每個(gè)文件調(diào)用func files = os.listdir(rootdir) # 獲取rootdir目錄中的所有文件 files.sort(lambda a, b: cmp(a.lower(), b.lower())) for file in files: fullpath = os.path.join(rootdir, file) if os.path.islink(fullpath): pass elif os.path.isdir(fullpath): find(func, fullpath, arg) elif os.path.isfile(fullpath): func(fullpath, arg) else: print 'find: cannot treat ', fullpath 上面的函數(shù)find可以從scitools模塊中獲取。與內(nèi)置函數(shù)os.path.walk相反,我們的find函數(shù)以大小寫敏感的字母順序來訪問文件和目錄。 我們可以使用find函數(shù)來列出所有大于1Mb的文件: def checksize2(fullpath, bigfiles): size = os.path.getsize(fullpath) if size > 1000000: bigfiles.append('%.2fMb %s' % (size/1000000.0, fullpath)) bigfiles = [] root = os.environ['HOME'] find(checksize2, root, bigfiles) for fileinfo in bigfiles: print fileinfo 參數(shù)arg帶來了巨大的靈活性。我們可以使用它來同時(shí)存放輸入數(shù)據(jù)和生成的數(shù)據(jù)結(jié)構(gòu)。下一個(gè)范例將收集所有大于一定尺寸的帶有規(guī)定擴(kuò)展名的文件的文件名和大小。輸出的結(jié)果按照文件大小排列。 bigfiles = {'filelist': [], # 文件名和大小列表 'extensions': ('.*ps', '.tiff', '.bmp'), 'size_limit': 1000000, # 1 Mb } find(checksize3, os.environ['HOME'], bigfiles) def checksize3(fullpath, arg): treat_file = False ext = os.path.splitext(fullpath)[1] import fnmatch # Unix的shell風(fēng)格的通配符匹配 for s in arg['extensions']: if fnmatch.fnmatch(ext, s): treat_file = True # fullpath帶有正確的擴(kuò)展名 size = os.path.getsize(fullpath) if treat_file and size > arg['size_limit']: size = '%.2fMb' % (size/1000000.0) # 打印 arg['filelist'].append({'size': size, 'name': fullpath}) # 按照大小排列文件 def filesort(a, b): return cmp(float(a['size'][:-2]), float(b['size'][:-2])) bigfiles['filelist'].sort(filesort) bigfiles['filelist'].reverse() for fileinfo in bigfiles['filelist']: print fileinfo['name'], fileinfo['size'] 注意為列表排序的函數(shù),bigfiles['filelist']函數(shù)中的每個(gè)元素就是一個(gè)字典,鍵size保存著一個(gè)字符串,不過在進(jìn)行比較之前我們必須將單位Mb(最后兩個(gè)字符)去掉,并將其轉(zhuǎn)換為浮點(diǎn)數(shù)。 八、小結(jié)
對(duì)于文件和目錄的處理,雖然可以通過操作系統(tǒng)命令來完成,但是Python語言為了便于開發(fā)人員以編程的方式處理相關(guān)工作,提供了許多處理文件和目錄的
內(nèi)置函數(shù)。重要的是,這些函數(shù)無論是在Unix、Windows還是Macintosh平臺(tái)上,它們的使用方式是完全一致的。本文詳細(xì)解釋了這些函數(shù)的使
用方法,其中,我們首先介紹了顯示目錄內(nèi)容的功能,然后描述如何測(cè)試一個(gè)文件名對(duì)應(yīng)的是一個(gè)標(biāo)準(zhǔn)文件、目錄還是鏈接,以及提取文件大小和日期的方法。之
后,我們還將介紹如何刪除文件和目錄,如何復(fù)制和刪除文件,以及怎樣將一個(gè)完整的文件路徑分解成目錄部分和文件名部分,最后,我們講解目錄的創(chuàng)建,以及如
何在目錄樹中移動(dòng)目錄并處理文件。
|