[Python]-10-檔案讀寫(中)

2021-05-05 12:00:12

引言

這篇文章介紹如何使用python的os與shutil模組,對檔案或資料夾進行讀寫、建立、刪除等操作。

文章目錄

0×2.資料夾相關操作
a.拼接路徑
b.獲取子目錄名
c.規範化路徑輸出
d.判斷目錄或檔案是否存在
e.列出目錄下所有檔案
f.建立刪除資料夾
g.萬用字元的使用
h.檔案和資料夾重新命名
i.資料夾複製

0×2.資料夾相關操作

a.拼接路徑

os.path.join()函數用於拼接路徑,它不會判斷兩個路徑是否存在,僅僅完成將前面的路徑和後面的路徑進行拼接的功能,請看下面的範例:

#!/usr/bin/env python
#coding=utf-8
import os
path1="/www"
path2="qingsword/com"
print(os.path.join(path1,path2))

#輸出
/www/qingsword/com

b.獲取子目錄名

有時候我們可能需要獲取某個路徑下最後一個子目錄的名稱,python提供了一個os.path.split()方法用於切割路徑,請看下面的範例:

#!/usr/bin/env python
#coding=utf-8
import os
path1="/www/qingsword/com"
#split()方法返回一個元組,包含兩個元素,父路徑和子目錄名
print(os.path.split(path1))
#可以直接使用兩個物件來接收元組中這兩個元素
p1,p2=os.path.split(path1)
print(p1)
print(p2)

#輸出
('/www/qingsword', 'com')
/www/qingsword
com

如果想遞迴獲取路徑中每一層目錄的名稱,可以使用下面的方法:

#!/usr/bin/env python
#coding=utf-8
import os
#------
def split_fully(path):
    """遞迴獲取路徑中每一層目錄的名稱,返回一個元組"""
    parent_path,dir_name=os.path.split(path)
    if dir_name=="":
        return (parent_path,)
    else:
        return split_fully(parent_path)+(dir_name,)

path1="/www/qingsword/com"
for p in split_fully(path1):
    print(p)

#輸出
/
www
qingsword
com

在上面這個範例中,程式的執行流程是這樣的,split_fully()函數首先將"/www/qingsword/com"路徑分割成"/www/qingsword"與"com",因為dir_name不為空,所以執行return split_fully(parent_path)+(dir_name,),這一句中(dir_name,)就相當於(com,)而前半段使用父路徑再次呼叫函數本身,外層函數將被掛起等待split_fully(parent_path)結果的返回,這一次的呼叫parent_path="/www/qingsword";

第一次呼叫自身"/www/qingsword"被分割成"/www"和"qingsword",由於dir_name不為空,函數第二次呼叫自身,此時如果我們將所有變數都代入資料,返回最外層被掛起的函數檢視return,應該是這樣的結構return split_fully("/www")+(qingsword,)+(com,);

第二次呼叫自身,"/www"被分割成parent_path="/",dir_name="www"不為空,程式會第三次呼叫自身,這一次parent_path,dir_name=os.path.split(path)分割後,dir_name為空,所以返回parent_path="/",最後一層一層返回,最外層的結構就變成了return ("/",)+("www",)+("qingsword",)+("com",),只需要遍歷這個元組,就能得到每個元素的值。

c.規範化路徑輸出

在路徑中,"."表示當前目錄,".."表示父目錄,如果我們不希望這些符號出現,可以使用os.path.normpath()函數,它不僅可以規範路徑,將兩個點自動解析成父目錄,還能去除路徑中多餘的斜槓,請看下面的範例:

#!/usr/bin/env python
#coding=utf-8
import os
path1="///www/./qingsword////hidden/..//com"
print(os.path.normpath(path1))

#normpath會自動清除目錄中多餘的斜槓,並且將..解析為父目錄,所以hidden目錄後面的..就相當於在hidden目錄裡面執行了一個"cd .."命令,返回了qingsword目錄中,而www目錄後面的.就代表當前目錄,所以直接去掉了,最後輸出如下
/www/qingsword/com

d.判斷目錄或檔案是否存在

os.path.exists()函數能夠判斷目標是否存在,但不能判斷目標是檔案還是目錄,請看下面的範例:

#!/usr/bin/env python
#coding=utf-8
import os
path1="/www/qingsword/com"
print(os.path.exists(path1))
print(os.path.exists("textfile"))

#輸出,如果目標存在就返回True,不存在就返回False
TRUE
FALSE

e.列出目錄下所有檔案

使用os.listdir()函數,可以返回目標目錄下所有資料夾和檔案的名稱列表,請看下面的範例:

#!/usr/bin/env python
#coding=utf-8
import os
#獲取當前指令碼所在目錄的絕對路徑
file_path=os.path.abspath(".")

#遍歷列表,列印出每個檔案或資料夾的名稱
for name in os.listdir(file_path):
    print(name)

#如果想列印出目錄下每個檔案或資料夾的完整路徑,可以修改print函數,下面兩種方法效果相同
print(file_path+"/"+name)
print(os.path.join(file_path,name))

如果想要對listdir的輸出進行排序,可以使用sorted()函數,預設按照字母順序排列,並且區分大小寫,大寫字母>符號>小寫字母,這樣for中的name會按照排序後的順序讀取並拼接:

#!/usr/bin/env python
#coding=utf-8
import os
file_path=os.path.abspath(".")
#排序輸出
for name in sorted(os.listdir(file_path)):
    print(os.path.join(file_path,name))

上面的範例僅僅能夠遍歷某個目錄下的檔案和目錄,但不能進行遞迴查詢,也就是說,這個目錄下的子目錄中的檔案是無法被搜尋到的,下面提供一種遍歷子目錄檔案的方法:

#!/usr/bin/env python
#coding=utf-8
import os
#------
def print_tree(path):
    """遞迴目錄"""
    if os.path.isdir(path):
        #遍歷目錄,列印絕對路徑
        for name in os.listdir(path):
            abs_path=os.path.join(path,name)
            print(abs_path)
            #如果路徑為目錄,呼叫自身,列印子目錄內容
            if os.path.isdir(abs_path):
                print_tree(abs_path)

print_tree("/home/qing/test")

再來看一個範例,讀取目錄下的檔案,顯示每個檔案的絕對路徑,大小和最後修改時間:

#!/usr/bin/env python
#coding=utf-8
import os
import time
#------
def list_dir(path):
    """列印目錄中檔案或資料夾的大小,最後修改時間和絕對路徑"""
    if os.path.isdir(path):
        for name in os.listdir(path):
            abs_path=os.path.join(path,name)
            sz=os.path.getsize(abs_path)
            modify_time=time.ctime(os.path.getmtime(abs_path))
            print("Size:%-8d Last Modify Time:%-25s Path:%s"%(sz,modify_time,abs_path))

list_dir("/home/qing/test")

利用列表生成式可以很簡潔的列印出某一型別的檔案列表,例如:

#!/usr/bin/env python3
#coding=utf-8
import os
#列出指令碼所在資料夾中所有檔案和資料夾名稱
print([x for x in os.listdir(os.path.abspath("."))])

#列出指令碼所在資料夾中所有子資料夾名稱
print([x for x in os.listdir(os.path.abspath("."))\
       if os.path.isdir(x)])

#列出指令碼所在資料夾中所有py檔名稱
print([x for x in os.listdir(os.path.abspath("."))\
       if os.path.isfile(x) and os.path.splitext(x)[1]==".py"])

f.建立刪除資料夾

建立資料夾:

#!/usr/bin/env python
#coding=utf-8
import os
#os.mkdir()函數一次只能建立一個資料夾
if not os.path.exists("qingsword"):
    os.mkdir("qingsword")
#os.makedirs()函數能夠一次性遞迴建立多個資料夾
if os.path.isdir("qingsword"):
    os.makedirs("qingsword/1/2/3/4")

Ps:在建立資料夾的過程中,如果目標存在,則會丟擲一個"FileExistsError"異常。

刪除資料夾:

#!/usr/bin/env python
#coding=utf-8
import os
import shutil

if os.path.isdir("qingsword"):
    os.rmdir("qingsword/1/2/3/4")
    shutil.rmtree("qingsword")

Ps:os.rmdir()函數只能刪除單個資料夾且資料夾下必須為空,否則會丟擲一個"OSError"異常,而shutil模組的rmtree方法能夠一次性刪除目錄及下面的所有檔案。

g.萬用字元的使用

在python路徑操作中,有下面這些比較常用的萬用字元:

  • 匹配多個字元;

? 匹配單個字元;

[...] 匹配括號中的單個字元,可以指定多個字元或字元範圍,例如:[ABCD]代表ABCD任意一個都能匹配;[a-z]匹配小寫字母表中任意一個字元;[0-9]匹配0-9數位中的任意一個;

[!...]不匹配括號中指定的任意一個或某個範圍中的一個;

在python中有一個萬用字元模組glob,這個模組的glob()函數同os.listdir()類似,都能返回某個目錄下的檔案,但glob.glob()函數更加強大,它能夠配合萬用字元返回指定的檔案型別,請看下面的範例:

#!/usr/bin/env python
#coding=utf-8
import os
import glob
path1="/home/qing/test"
if os.path.isdir(path1):
    #返回path1目錄下所有py字尾的檔案
    print(glob.glob(os.path.join(path1,"*.py")))
    #返回檔名最後一個字元在a-d範圍內的py檔案
    print(glob.glob(os.path.join(path1,"*[a-d].py")))
    #返回檔名為四個字元的py檔案
    print(glob.glob(os.path.join(path1,"????.py")))
    返回檔名為三個字元且最後一個字元不在b-f範圍內的py檔案
    print(glob.glob(os.path.join(path1,"??[!b-f].py")))
    #這是glob函數強大的地方,遞迴path1目錄,返回目錄以及子目錄中所有py檔案
    print(glob.glob(os.path.join(path1,"*/*.py")))

Ps:glob.glob()函數返回型別為列表。

h.檔案和資料夾重新命名

os模組的rename方法可以實現檔案和資料夾重新命名的操作,請看下面的範例:

#!/usr/bin/env python3
#coding=utf-8
import os
#如果file1存在且file2不存在,將file1改名為file2,file1可以是檔案或資料夾的絕對或相對路徑
if os.path.exists("file1") and not os.path.exists("file2"):
    os.rename("file1","file2")

i.資料夾複製

可以使用shutil模組的copytree方法來拷貝一個目錄到目標位置,例如:

#!/usr/bin/env python3
#coding=utf-8
import os
import shutil
def copyDir(src,dst):
    """完成原始檔夾到目標位置的複製"""
    if os.path.isdir(src) and not os.path.exists(dst):
        shutil.copytree(src,dst)
        print("複製成功。")
    else:
        print("複製失敗。")
def moveDir(src,dst):
    """完成原始檔夾到目標位置的移動"""
    if os.path.isdir(src) and not os.path.exists(dst):
        shutil.copytree(src,dst)
        shutil.rmtree(src)
        print("移動成功。")
    else:
        print("移動失敗。")   

moveDir("dir1","dir2")
copyDir("dir1","dir2")