主頁 > 後端開發 > Python檔案操作方法,看這篇就足夠

Python檔案操作方法,看這篇就足夠

2020-10-05 10:39:40 後端開發

Python中有幾個內置模塊和方法來處理檔案,這些方法被分割到例如osos.path , shutil 和 pathlib 等等幾個模塊中,文章將列舉Python中對檔案最常用的操作和方法,


在這篇文章中,你將學習如何:

  • 獲取檔案屬性
  • 創建目錄
  • 檔案名模式匹配
  • 遍歷目錄樹
  • 創建臨時檔案和目錄
  • 洗掉檔案和目錄
  • 復制、移動和重命名檔案和目錄
  • 創建和解壓ZIP和TAR檔案
  • 使用fileinput 模塊打開多個檔案
    這里要注意不管你是為了Python就業還是興趣愛好,記住:專案開發經驗永遠是核心,如果如果你沒有2020最新python入門到高級實戰視頻教程,可以去小編的Python交流.裙 :七衣衣九七七巴而五(數字的諧音)轉換下可以找到了,里面很多新python教程專案,還可以跟老司機交流討教!

Python中檔案資料的讀和寫

使用Python對檔案進行讀和寫是十分簡單的,為此,你首先必須使用合適的模式打開檔案,這里有一個如何打開文本檔案并讀取其內容的例子,

with open('data.txt', 'r') as f:
    data = https://www.cnblogs.com/chengxuyuanaa/p/f.read()
    print('context: {}'.format(data))
復制代碼

open() 接收一個檔案名和一個模式作為它的引數,r 表示以只讀模式打開檔案,想要往檔案中寫資料的話,則用w 作為引數,

with open('data.txt', 'w') as f:
    data = https://www.cnblogs.com/chengxuyuanaa/p/'some data to be written to the file'
    f.write(data)
復制代碼

在上述例子中,open()打開用于讀取或寫入的檔案并回傳檔案句柄(本例子中的 f ),該句柄提供了可用于讀取或寫入檔案資料的方法,閱讀 Working With File I/O in Python 獲取更多關于如何讀寫檔案的資訊,


獲取目錄串列

假設你當前的作業目錄有一個叫 my_directory 的子目錄,該目錄包含如下內容:

.
├── file1.py
├── file2.csv
├── file3.txt
├── sub_dir
│   ├── bar.py
│   └── foo.py
├── sub_dir_b
│   └── file4.txt
└── sub_dir_c
    ├── config.py
    └── file5.txt
復制代碼

Python內置的 os 模塊有很多有用的方法能被用來列出目錄內容和過濾結果,為了獲取檔案系統中特定目錄的所有檔案和檔案夾串列,可以在遺留版本的Python中使用 os.listdir() 或 在Python 3.x 中使用 os.scandir() , 如果你還想獲取檔案和目錄屬性(如檔案大小和修改日期),那么 os.scandir() 則是首選的方法,

使用遺留版本的Python獲取目錄串列

import os
entries = os.listdir('my_directory')
復制代碼

os.listdir() 回傳一個Python串列,其中包含path引數所指目錄的檔案和子目錄的名稱,

['file1.py', 'file2.csv', 'file3.txt', 'sub_dir', 'sub_dir_b', 'sub_dir_c']
復制代碼

目錄串列現在看上去不容易閱讀,對 os.listdir() 的呼叫結果使用回圈列印有助于查看,

for entry in entries:
    print(entry)

"""
file1.py
file2.csv
file3.txt
sub_dir
sub_dir_b
sub_dir_c
"""

復制代碼

使用現代版本的Python獲取目錄串列

在現代Python版本中,可以使用 os.scandir() 和 pathlib.Path 來替代 os.listdir() ,

os.scandir() 在Python 3.5 中被參考,其檔案為 PEP 471 ,

os.scandir() 呼叫時回傳一個迭代器而不是一個串列,

import os
entries = os.scandir('my_directory')
print(entries)
# <posix.ScandirIterator at 0x105b4d4b0>
復制代碼

ScandirIterator 指向了當前目錄中的所有條目,你可以遍歷迭代器的內容,并列印檔案名,

import os
with os.scandir('my_directory') as entries:
    for entry in entries:
        print(entry.name)
復制代碼

這里 os.scandir() 和with陳述句一起使用,因為它支持背景關系管理協議,使用背景關系管理器關閉迭代器并在迭代器耗盡后自動釋放獲取的資源,在 my_directory 列印檔案名的結果就和在 os.listdir() 例子中看到的一樣:

file1.py
file2.csv
file3.txt
sub_dir
sub_dir_b
sub_dir_c
復制代碼

另一個獲取目錄串列的方法是使用 pathlib 模塊:

from pathlib import Path

entries = Path('my_directory')
for entry in entries.iterdir():
    print(entry.name)
復制代碼

pathlib.Path() 回傳的是 PosixPath 或 WindowsPath 物件,這取決于作業系統,

pathlib.Path() 物件有一個 .iterdir() 的方法用于創建一個迭代器包含該目錄下所有檔案和目錄,由 .iterdir() 生成的每個條目都包含檔案或目錄的資訊,例如其名稱和檔案屬性,pathlib 在Python3.4時被第一次引入,并且是對Python一個很好的加強,它為檔案系統提供了面向物件的介面,

在上面的例子中,你呼叫 pathlib.Path() 并傳入了一個路徑引數,然后呼叫 .iterdir() 來獲取 my_directory 下的所有檔案和目錄串列,

pathlib 提供了一組類,以簡單并且面向物件的方式提供了路徑上的大多數常見的操作,使用 pathlib 比起使用 os 中的函式更加有效,和 os 相比,使用 pathlib 的另一個好處是減少了操作檔案系統路徑所匯入包或模塊的數量,想要了解更多資訊,可以閱讀 Python 3’s pathlib Module: Taming the File System ,

運行上述代碼會得到如下結果:

file1.py
file2.csv
file3.txt
sub_dir
sub_dir_b
sub_dir_c
復制代碼

使用 pathlib.Path() 或 os.scandir() 來替代 os.listdir() 是獲取目錄串列的首選方法,尤其是當你需要獲取檔案型別和檔案屬性資訊的時候,pathlib.Path() 提供了在 os 和 shutil 中大部分處理檔案和路徑的功能,并且它的方法比這些模塊更加有效,我們將討論如何快速的獲取檔案屬性,

函式描述
os.listdir() 以串列的方式回傳目錄中所有的檔案和檔案夾
os.scandir() 回傳一個迭代器包含目錄中所有的物件,物件包含檔案屬性資訊
pathlib.Path().iterdir() 回傳一個迭代器包含目錄中所有的物件,物件包含檔案屬性資訊

這些函式回傳目錄中所有內容的串列,包括子目錄,這可能并總是你一直想要的結果,下一節將向你展示如何從目錄串列中過濾結果,

列出目錄中的所有檔案

這節將向你展示如何使用 os.listdir() ,os.scandir() 和 pathlib.Path() 列印出目錄中檔案的名稱,為了過濾目錄并僅列出 os.listdir() 生成的目錄串列的檔案,要使用 os.path :

import os

basepath = 'my_directory'
for entry in os.listdir(basepath):
    # 使用os.path.isfile判斷該路徑是否是檔案型別
    if os.path.isfile(os.path.join(base_path, entry)):
        print(entry)
復制代碼

在這里呼叫 os.listdir() 回傳指定路徑中所有內容的串列,接著使用 os.path.isfile() 過濾串列讓其只顯示檔案型別而非目錄型別,代碼執行結果如下:

file1.py
file2.csv
file3.txt
復制代碼

一個更簡單的方式來列出一個目錄中所有的檔案是使用 os.scandir() 或 pathlib.Path() :

import os

basepath = 'my_directory'
with os.scandir(basepath) as entries:
    for entry in entries:
        if entry.is_file():
            print(entry.name)
復制代碼

使用 os.scandir() 比起 os.listdir() 看上去更清楚和更容易理解,對 ScandirIterator 的每一項呼叫 entry.isfile() ,如果回傳 True 則表示這一項是一個檔案,上述代碼的輸出如下:

file1.py
file3.txt
file2.csv
復制代碼

接著,展示如何使用 pathlib.Path() 列出一個目錄中的檔案:

from pathlib import Path

basepath = Path('my_directory')
for entry in basepath.iterdir():
    if entry.is_file():
        print(entry.name)
復制代碼

在 .iterdir() 產生的每一項呼叫 .is_file() ,產生的輸出結果和上面相同:

file1.py
file3.txt
file2.csv
復制代碼

如果將for回圈和if陳述句組合成單個生成器運算式,則上述的代碼可以更加簡潔,關于生成器運算式,推薦一篇Dan Bader 的文章,

修改后的版本如下:

from pathlib import Path

basepath = Path('my_directory')
files_in_basepath = (entry for entry in basepath.iterdir() if entry.is_file())
for item in files_in_basepath:
    print(item.name)
復制代碼

上述代碼的執行結果和之前相同,本節展示使用 os.scandir() 和 pathlib.Path() 過濾檔案或目錄比使用 os.listdir() 和 os.path 更直觀,代碼看起來更簡潔,

列出子目錄

如果要列出子目錄而不是檔案,請使用下面的方法,現在展示如何使用 os.listdir() 和 os.path() :

import os

basepath = 'my_directory'
for entry in os.listdir(basepath):
    if os.path.isdir(os.path.join(basepath, entry)):
        print(entry)
復制代碼

當你多次呼叫 os.path,join() 時,以這種方式操作檔案系統就會變得很笨重,在我電腦上運行此代碼會產生以下輸出:

sub_dir
sub_dir_b
sub_dir_c
復制代碼

下面是如何使用 os.scandir() :

import os

basepath = 'my_directory'
with os.scandir(basepath) as entries:
    for entry in entries:
        if entry.is_dir():
            print(entry.name)
復制代碼

與檔案串列中的示例一樣,此處在 os.scandir() 回傳的每一項上呼叫 .is_dir() ,如果這項是目錄,則 is_dir() 回傳 True,并列印出目錄的名稱,輸出結果和上面相同:

sub_dir_c
sub_dir_b
sub_dir
復制代碼

下面是如何使用 pathlib.Path() :

from pathlib import Path

basepath = Path('my_directory')
for entry in basepath.iterdir():
    if entry.is_dir():
        print(entry.name)
復制代碼

在 .iterdir() 迭代器回傳的每一項上呼叫 is_dir() 檢查是檔案還是目錄,如果該項是目錄,則列印其名稱,并且生成的輸出與上一示例中的輸出相同:

sub_dir_c
sub_dir_b
sub_dir
復制代碼

獲取檔案屬性

Python可以很輕松的獲取檔案大小和修改時間等檔案屬性,可以通過使用 os.stat() , os.scandir() 或 pathlib.Path 來獲取,

os.scandir() 和 pathlib.Path() 能直接獲取到包含檔案屬性的目錄串列,這可能比使用 os.listdir() 列出檔案然后獲取每個檔案的檔案屬性資訊更加有效,

下面的例子顯示了如何獲取 my_directory 中檔案的最后修改時間,以時間戳的方式輸出:

import os

with os.scandir('my_directory') as entries:
    for entry in entries:
        info = entry.stat()
        print(info.st_mtime)
        
"""
1548163662.3952665
1548163689.1982062
1548163697.9175904
1548163721.1841028
1548163740.765162
1548163769.4702623
"""
復制代碼

os.scandir() 回傳一個 ScandirIterator 物件,ScandirIterator 物件中的每一項有 .stat() 方法能獲取關于它指向檔案或目錄的資訊,.stat() 提供了例如檔案大小和最后修改時間的資訊,在上面的示例中,代碼列印了 st_time 屬性,該屬性是上次修改檔案內容的時間,

pathlib 模塊具有相應的方法,用于獲取相同結果的檔案資訊:

from pathlib import Path

basepath = Path('my_directory')
for entry in basepath.iterdir():
    info = entry.stat()
    print(info.st_mtime)

"""
1548163662.3952665
1548163689.1982062
1548163697.9175904
1548163721.1841028
1548163740.765162
1548163769.4702623
"""
復制代碼

在上面的例子中,回圈 .iterdir() 回傳的迭代器并通過對其中每一項呼叫 .stat() 來獲取檔案屬性,st_mtime 屬性是一個浮點型別的值,表示的是時間戳,為了讓 st_time 回傳的值更容易閱讀,你可以撰寫一個輔助函式將其轉換為一個 datetime 物件:

import datetime                                                                   
from pathlib import Path                                                          
                                                                                  
                                                                                  
def timestamp2datetime(timestamp, convert_to_local=True, utc=8, is_remove_ms=True)
    """                                                                           
    轉換 UNIX 時間戳為 datetime物件                                                       
    :param timestamp: 時間戳                                                         
    :param convert_to_local: 是否轉為本地時間                                             
    :param utc: 時區資訊,中國為utc+8                                                     
    :param is_remove_ms: 是否去除毫秒                                                   
    :return: datetime 物件                                                          
    """                                                                           
    if is_remove_ms:                                                              
        timestamp = int(timestamp)                                                
    dt = datetime.datetime.utcfromtimestamp(timestamp)                            
    if convert_to_local:                                                          
        dt = dt + datetime.timedelta(hours=utc)                                   
    return dt                                                                     
                                                                                  
                                                                                  
def convert_date(timestamp, format='%Y-%m-%d %H:%M:%S'):                          
    dt = timestamp2datetime(timestamp)                                            
    return dt.strftime(format)                                                    
                                                                                  
                                                                                  
basepath = Path('my_directory')                                                   
for entry in basepath.iterdir():
    if entry.is_file()
    	info = entry.stat()                                                           
    	print('{} 上次修改時間為 {}'.format(entry.name, timestamp2datetime(info.st_mtime)))  
復制代碼

首先得到 my_directory 中檔案的串列以及它們的屬性,然后呼叫 convert_date() 來轉換檔案最后修改時間讓其以一種人類可讀的方式顯示,convert_date() 使用 .strftime() 將datetime型別轉換為字串,

上述代碼的輸出結果:

file3.txt 上次修改時間為 2019-01-24 09:04:39
file2.csv 上次修改時間為 2019-01-24 09:04:39
file1.py 上次修改時間為 2019-01-24 09:04:39
復制代碼

將日期和時間轉換為字串的語法可能會讓你感到混亂,如果要了解更多的資訊,請查詢相關的官方檔案 ,另一個方式則是閱讀 strftime.org ,


創建目錄

你撰寫的程式遲早需要創建目錄以便在其中存盤資料, os 和 pathlib 包含了創建目錄的函式,我們將會考慮如下方法:

方法描述
os.mkdir() 創建單個子目錄
os.makedirs() 創建多個目錄,包括中間目錄
Pathlib.Path.mkdir() 創建單個或多個目錄

創建單個目錄

要創建單個目錄,把目錄路徑作為引數傳給 os.mkdir() :

import os

os.mkdir('example_directory')
復制代碼

如果該目錄已經存在,os.mkdir() 將拋出 FileExistsError 例外,或者,你也可以使用 pathlib 來創建目錄:

from pathlib import Path

p = Path('example_directory')
p.mkdir()
復制代碼

如果路徑已經存在,mkdir() 會拋出 FileExistsError 例外:

FileExistsError: [Errno 17] File exists: 'example_directory'
復制代碼

為了避免像這樣的錯誤拋出, 當發生錯誤時捕獲錯誤并讓你的用戶知道:

from pathlib import Path

p = Path('example_directory')
try:
    p.mkdir()
except FileExistsError as e:
    print(e)
復制代碼

或者,你可以給 .mkdir() 傳入 exist_ok=True 引數來忽略 FileExistsError 例外:

from pathlib import Path

p = Path('example_directory')
p.mkdir(exist_ok=True)
復制代碼

如果目錄已存在,則不會引起錯誤,

創建多個目錄

os.makedirs() 和 os.mkdir() 類似,兩者之間的區別在于,os.makedirs() 不僅可以創建單獨的目錄,還可以遞回的創建目錄樹,換句話說,它可以創建任何必要的中間檔案夾,來確保存在完整的路徑,

os.makedirs() 和在bash中運行 mkdir -p 類似,例如,要創建一組目錄像 2018/10/05,你可以像下面那樣操作:

import os

os.makedirs('2018/10/05', mode=0o770)
復制代碼

上述代碼創建了 2018/10/05 的目錄結構并為所有者和組用戶提供讀、寫和執行權限,默認的模式為 0o777 ,增加了其他用戶組的權限,有關檔案權限以及模式的應用方式的更多詳細資訊,請參考 檔案 ,

運行 tree 命令確認我們應用的權限:

$ tree -p -i .
.
[drwxrwx---]  2018
[drwxrwx---]  10
[drwxrwx---]  05
復制代碼

上述代碼列印出當前目錄的目錄樹, tree 通常被用來以樹形結構列出目錄的內容,傳入 -p 和 -i 引數則會以垂直串列列印出目錄名稱以及其檔案權限資訊,-p 用于輸出檔案權限,-i 則用于讓 tree 命令產生一個沒有縮進線的垂直串列,

正如你所看到的,所有的目錄都擁有 770 權限,另一個方式創建多個目錄是使用 pathlib.Path 的 .mkdir() :

from pathlib import Path

p = Path('2018/10/05')
p.mkdir(parents=True, exist_ok=True)
復制代碼

通過給 Path.mkdir() 傳遞 parents=True 關鍵字引數使它創建 05 目錄和使其路徑有效的所有父級目錄,

在默認情況下,os.makedirs() 和 pathlib.Path.mkdir() 會在目標目錄存在的時候拋出 OSError ,通過每次呼叫函式時傳遞 exist_ok=True 作為關鍵字引數則可以覆寫此行為(從Python3.2開始),

運行上述代碼會得到像下面的結構:

└── 2018
    └── 10
        └── 05
復制代碼

我更喜歡在創建目錄時使用 pathlib ,因為我可以使用相同的函式方法來創建一個或多個目錄,


檔案名模式匹配

使用上述方法之一獲取目錄中的檔案串列后,你可能希望搜索和特定的模式匹配的檔案,

下面這些是你可以使用的方法和函式:

  • endswith() 和 startswith() 字串方法
  • fnmatch.fnmatch()
  • glob.glob()
  • pathlib.Path.glob()

這些方法和函式是下面要討論的,本小節的示例將在名為 some_directory 的目錄下執行,該目錄具有以下的結構:

.
├── admin.py
├── data_01_backup.txt
├── data_01.txt
├── data_02_backup.txt
├── data_02.txt
├── data_03_backup.txt
├── data_03.txt
├── sub_dir
│   ├── file1.py
│   └── file2.py
└── tests.py
復制代碼

如果你正在使用 Bash shell,你可以使用以下的命令創建上述目錄結構:

mkdir some_directory
cd some_directory
mkdir sub_dir
touch sub_dir/file1.py sub_dir/file2.py
touch data_{01..03}.txt data_{01..03}_backup.txt admin.py tests.py
復制代碼

這將會創建 some_directory 目錄并進入它,接著創建 sub_dir ,下一行在 sub_dir 創建 file1.py 和 file2.py ,最后一行使用擴展創建其它所有檔案,想要學習更多關于shell擴展,請閱讀 這里 ,

使用字串方法

Python有幾個內置 修改和操作字串 的方法,當在匹配檔案名時,其中的兩個方法 .startswith() 和 .endswith() 非常有用,要做到這點,首先要獲取一個目錄串列,然后遍歷,

import os

for f_name in os.listdir('some_directory'):
    if f_name.endswith('.txt'):
        print(f_name)
復制代碼

上述代碼找到 some_directory 中的所有檔案,遍歷并使用 .endswith() 來列印所有擴展名為 .txt 的檔案名,運行代碼在我的電腦上輸出如下:

data_01.txt
data_01_backup.txt
data_02.txt
data_02_backup.txt
data_03.txt
data_03_backup.txt
復制代碼

使用 fnmatch 進行簡單檔案名模式匹配

字串方法匹配的能力是有限的,fnmatch 有對于模式匹配有更先進的函式和方法,我們將考慮使用 fnmatch.fnmatch() ,這是一個支持使用 * 和 ? 等通配符的函式,例如,使用 fnmatch 查找目錄中所有 .txt 檔案,你可以這樣做:

import os
import fnmatch

for f_name in os.listdir('some_directory'):
    if fnmatch.fnmatch(f_name, '*.txt'):
        print(f_name)
復制代碼

迭代 some_directory 中的檔案串列,并使用 .fnmatch() 對擴展名為 .txt 的檔案執行通配符搜索,

更先進的模式匹配

假設你想要查找符合特定掉件的 .txt 檔案,例如,你可能指向找到包含單次 data 的 .txt檔案,一組下劃線之間的數字,以及檔案名中包含單詞 backup ,就類似于 data_01_backupdata_02_backup, 或 data_03_backup ,

你可以這樣使用 fnmatch.fnmatch() :

import os
import fnmatch

for f_name in os.listdir('some_directory'):
    if fnmatch.fnmatch(f_name, 'data_*_backup.txt'):
        print(f_name)
復制代碼

這里就僅僅列印出匹配 data_*_backup.txt 模式的檔案名稱,模式中的 * 將匹配任何字符,因此運行這段代碼則將查找檔案名以 data 開頭并以 backup.txt 的所有文本檔案,就行下面的輸出所示 :

data_01_backup.txt
data_02_backup.txt
data_03_backup.txt
復制代碼

使用 glob 進行檔案名模式匹配

另一個有用的模式匹配模塊是 glob ,

.glob() 在 glob 模塊中的左右就像 fnmatch.fnmatch(),但是與 fnmach.fnmatch() 不同的是,它將以 . 開頭的檔案視為特殊檔案,

UNIX和相關系統在檔案串列中使用通配符像 ? 和 * 表示全匹配,

例如,在UNIX shell中使用 mv *.py python_files 移動所有 .py 擴展名 的檔案從當前目錄到 python_files ,這 * 是一個通配符表示任意數量的字符,*.py 是一個全模式,Windows作業系統中不提供此shell功能,但 glob 模塊在Python中添加了此功能,使得Windows程式可以使用這個特性,

這里有一個使用 glob 模塊在當前目錄下查詢所有Python代碼檔案:

import glob

print(glob.glob('*.py'))
復制代碼

glob.glob('*.py') 搜索當前目錄中具有 .py 擴展名的檔案,并且將它們以串列的形式回傳, glob 還支持 shell 樣式的通配符來進行匹配 :

import glob

for name in glob.glob('*[0-9]*.txt'):
    print(name)
復制代碼

這將找到所有檔案名中包含數字的文本檔案(.txt) :

data_01.txt
data_01_backup.txt
data_02.txt
data_02_backup.txt
data_03.txt
data_03_backup.txt
復制代碼

glob 也很容易在子目錄中遞回的搜索檔案:

import glob

for name in glob.iglob('**/*.py', recursive=True):
    print(name)
復制代碼

這里例子使用了 glob.iglob() 在當前目錄和子目錄中搜索所有的 .py 檔案,傳遞 recursive=True 作為 .iglob() 的引數使其搜索當前目錄和子目錄中的 .py 檔案,glob.glob() 和 glob.iglob() 不同之處在于,iglob() 回傳一個迭代器而不是一個串列,

運行上述代碼會得到以下結果:

admin.py
tests.py
sub_dir/file1.py
sub_dir/file2.py
復制代碼

pathlib 也包含類似的方法來靈活的獲取檔案串列,下面的例子展示了你可以使用 .Path.glob() 列出以字母 p 開始的檔案型別的檔案串列,

from pathlib import Path

p = Path('.')

for name in p.glob('*.p*'):
    print(name)
復制代碼

呼叫 p.glob('*.p*') 會回傳一個指向當前目錄中所有擴展名以字母 p 開頭的檔案的生成器物件,

Path.glob() 和上面討論過的 os.glob() 類似,正如你看到的, pathlib 混合了許多 os , os.path 和 glob 模塊的最佳特性到一個模塊中,這使得使用起來很方便,

回顧一下,這是我們在本節中介紹的功能表:

函式描述
startswith() 測驗一個字串是否以一個特定的模式開始,回傳 True 或 False
endswith() 測驗一個字串是否以一個特定的模式結束,回傳 True 或 False
fnmatch.fnmatch(filename, pattern) 測驗檔案名是否匹配這個模式,回傳 True 或 False
glob.glob() 回傳一個匹配該模式的檔案名串列
pathlib.Path.glob() 回傳一個匹配該模式的生成器物件

遍歷目錄和處理檔案

一個常見的編程任務是遍歷目錄樹并處理目錄樹中的檔案,讓我們來探討一下如何使用內置的Python函式 os.walk() 來實作這一功能,os.walk() 用于通過從上到下或從下到上遍歷樹來生成目錄樹中的檔案名,處于本節的目的,我們想操作以下的目錄樹:

├── folder_1
│   ├── file1.py
│   ├── file2.py
│   └── file3.py
├── folder_2
│   ├── file4.py
│   ├── file5.py
│   └── file6.py
├── test1.txt
└── test2.txt
復制代碼

以下是一個示例,演示如何使用 os.walk() 列出目錄樹中的所有檔案和目錄,

os.walk() 默認是從上到下遍歷目錄:

import os
for dirpath, dirname, files in os.walk('.'):
   print(f'Found directory: {dirpath}')
   for file_name in files:
       print(file_name)
復制代碼

os.walk() 在每個回圈中回傳三個值:

  1. 當前檔案夾的名稱
  2. 當前檔案夾中子檔案夾的串列
  3. 當前檔案夾中檔案的串列

在每次迭代中,會列印出它找到的子目錄和檔案的名稱:

Found directory: .
test1.txt
test2.txt
Found directory: ./folder_1
file1.py
file3.py
file2.py
Found directory: ./folder_2
file4.py
file5.py
file6.py
復制代碼

要以自下而上的方式遍歷目錄樹,則將 topdown=False 關鍵字引數傳遞給 os.walk() :

for dirpath, dirnames, files in os.walk('.', topdown=False):
    print(f'Found directory: {dirpath}')
    for file_name in files:
        print(file_name)
復制代碼

傳遞 topdown=False 引數將使 os.walk() 首先列印出它在子目錄中找到的檔案:

Found directory: ./folder_1
file1.py
file3.py
file2.py
Found directory: ./folder_2
file4.py
file5.py
file6.py
Found directory: .
test1.txt
test2.txt
復制代碼

如你看見的,程式在列出根目錄的內容之前列出子目錄的內容, 這在在你想要遞回洗掉檔案和目錄的情況下非常有用, 你將在以下部分中學習如何執行此操作, 默認情況下,os.walk 不會訪問通過軟連接創建的目錄, 可以通過使用 followlinks = True 引數來覆寫默認行為,


創建臨時檔案和目錄

Python提供了 tempfile 模塊來便捷的創建臨時檔案和目錄,

tempfile 可以在你程式運行時打開并存盤臨時的資料在檔案或目錄中, tempfile 會在你程式停止運行后洗掉這些臨時檔案,

現在,讓我們看看如何創建一個臨時檔案:

from tempfile import  TemporaryFile

# 創建一個臨時檔案并為其寫入一些資料
fp = TemporaryFile('w+t')
fp.write('Hello World!')
# 回到開始,從檔案中讀取資料
fp.seek(0)
data = https://www.cnblogs.com/chengxuyuanaa/p/fp.read()
print(data)
# 關閉檔案,之后他將會被洗掉
fp.close()
復制代碼

第一步是從 tempfile 模塊匯入 TemporaryFile , 接下來,使用 TemporaryFile() 方法并傳入一個你想打開這個檔案的模式來創建一個類似于物件的檔案,這將創建并打開一個可用作臨時存盤區域的檔案,

在上面的示例中,模式為 w + t,這使得 tempfile 在寫入模式下創建臨時文本檔案, 沒有必要為臨時檔案提供檔案名,因為在腳本運行完畢后它將被銷毀,

寫入檔案后,您可以從中讀取并在完成處理后將其關閉, 一旦檔案關閉后,將從檔案系統中洗掉, 如果需要命名使用 tempfile 生成的臨時檔案,請使用 tempfile.NamedTemporaryFile() ,

使用 tempfile 創建的臨時檔案和目錄存盤在用于存盤臨時檔案的特殊系統目錄中, Python將在目錄串列搜索用戶可以在其中創建檔案的目錄,

在Windows上,目錄按順序為 C:\TEMPC:\TMP\TEMP 和 \TMP, 在所有其他平臺上,目錄按順序為 / tmp/var/tmp 和 /usr/tmp , 如果上述目錄中都沒有,tempfile 將在當前目錄中存盤臨時檔案和目錄,

.TemporaryFile() 也是一個背景關系管理器,因此它可以與with陳述句一起使用, 使用背景關系管理器會在讀取檔案后自動關閉和洗掉檔案:

with TemporaryFile('w+t') as fp:
    fp.write('Hello universe!')
    fp.seek(0)
    fp.read()
# 臨時檔案現在已經被關閉和洗掉
復制代碼

這將創建一個臨時檔案并從中讀取資料, 一旦讀取檔案的內容,就會關閉臨時檔案并從檔案系統中洗掉,

tempfile 也可用于創建臨時目錄, 讓我們看一下如何使用 tempfile.TemporaryDirectory()來做到這一點:

import tempfile
import os

tmp = ''
with tempfile.TemporaryDirectory() as tmpdir:
    print('Created temporary directory ', tmpdir)
    tmp = tmpdir
    print(os.path.exists(tmpdir))

print(tmp)
print(os.path.exists(tmp))
復制代碼

呼叫 tempfile.TemporaryDirectory() 會在檔案系統中創建一個臨時目錄,并回傳一個表示該目錄的物件, 在上面的示例中,使用背景關系管理器創建目錄,目錄的名稱存盤在 tmpdir 變數中, 第三行列印出臨時目錄的名稱,os.path.exists(tmpdir) 來確認目錄是否實際在檔案系統中創建,

在背景關系管理器退出背景關系后,臨時目錄將被洗掉,并且對 os.path.exists(tmpdir)的呼叫將回傳False,這意味著該目錄已成功洗掉,


洗掉檔案和目錄

您可以使用 osshutil 和 pathlib 模塊中的方法洗掉單個檔案,目錄和整個目錄樹, 以下將介紹如何洗掉你不再需要的檔案和目錄,

Python中洗掉檔案

要洗掉單個檔案,請使用 pathlib.Path.unlink()os.remove() 或 os.unlink()

os.remove() 和 os.unlink() 在語意上是相同的, 要使用 os.remove()洗掉檔案,請執行以下操作:

import os

data_file = 'C:\\Users\\vuyisile\\Desktop\\Test\\data.txt'
os.remove(data_file)
復制代碼

使用 os.unlink() 洗掉檔案與使用 os.remove() 的方式類似:

import os

data_file = 'C:\\Users\\vuyisile\\Desktop\\Test\\data.txt'
os.unlink(data_file)
復制代碼

在檔案上呼叫 .unlink() 或 .remove() 會從檔案系統中洗掉該檔案, 如果傳遞給它們的路徑指向目錄而不是檔案,這兩個函式將拋出 OSError , 為避免這種情況,可以檢查你要洗掉的內容是否是檔案,并在確認是檔案時執行洗掉操作,或者可以使用例外處理來處理 OSError :

import os

data_file = 'home/data.txt'
# 如果型別是檔案則進行洗掉
if os.path.is_file(data_file):
    os.remove(data_file)
else:
    print(f'Error: {data_file} not a valid filename')
復制代碼

os.path.is_file() 檢查 data_file 是否實際上是一個檔案, 如果是,則通過呼叫 os.remove() 洗掉它, 如果 data_file 指向檔案夾,則會向控制臺輸出錯誤訊息,

以下示例說明如何在洗掉檔案時使用例外處理來處理錯誤:

import os

data_file = 'home/data.txt'
# 使用例外處理
try:
    os.remove(data_file)
except OSError as e:
    print(f'Error: {data_file} : {e.strerror}')
復制代碼

上面的代碼嘗試在檢查其型別之前先洗掉該檔案, 如果 data_file 實際上不是檔案,則拋出的 OSError 將在except子句中處理,并向控制臺輸出錯誤訊息, 列印出的錯誤訊息使用 Python f-strings 格式化,

最后,你還可以使用 pathlib.Path.unlink() 洗掉檔案:

from pathlib import Path

data_file = Path('home/data.txt')
try:
    data_file.unlink()
except IsADirectoryError as e:
    print(f'Error: {data_file} : {e.strerror}')
復制代碼

這將創建一個名為 data_file 的 Path 物件,該物件指向一個檔案, 在 data_file 上呼叫.unlink()將洗掉 home / data.txt , 如果 data_file 指向目錄,則引發 IsADirectoryError , 值得注意的是,上面的Python程式和運行它的用戶具有相同的權限, 如果用戶沒有洗掉檔案的權限,則會引發 PermissionError ,

洗掉目錄

標準庫提供了一下函式來洗掉目錄:

  • os.rmdir()
  • pathlib.Path.rmdir()
  • shutil.rmtree()

要洗掉單個目錄或檔案夾可以使用 os.rmdir() 或 pathlib.Path.rmdir() ,這兩個函式只在你洗掉空目錄的時候有效,如果目錄不為空,則會拋出 OSError ,下面演示如何洗掉一個檔案夾:

import os

trash_dir = 'my_documents/bad_dir'

try:
    os.rmdir(trash_dir)
except OSError as e:
    print(f'Error: {trash_dir} : {e.strerror}')
復制代碼

現在,trash_dir 已經通過 os.rmdir() 被洗掉了,如果目錄不為空,則會在螢屏上列印錯誤資訊:

Traceback (most recent call last):
  File '<stdin>', line 1, in <module>
OSError: [Errno 39] Directory not empty: 'my_documents/bad_dir'
復制代碼

同樣,你也可使用 pathlib 來洗掉目錄:

from pathlib import Path

trash_dir = Path('my_documents/bad_dir')

try:
    trash_dir.rmdir()
except OSError as e:
    print(f'Error: {trash_dir} : {e.strerror}')
復制代碼

這里創建了一個 Path 物件指向要被洗掉的目錄,如果目錄為空,呼叫 Path 物件的 .rmdir() 方法洗掉它,

洗掉完整的目錄樹

要洗掉非空目錄和完整的目錄樹,Python提供了 shutil.rmtree() :

import shutil

trash_dir = 'my_documents/bad_dir'

try:
    shutil.rmtree(trash_dir)
except OSError as e:
    print(f'Error: {trash_dir} : {e.strerror}')
復制代碼

當呼叫 shutil.rmtree() 時,trash_dir 中的所有內容都將被洗掉, 在某些情況下,你可能希望以遞回方式洗掉空檔案夾, 你可以使用上面討論的方法之一結合 os.walk() 來完成此操作:

import os

for dirpath, dirnames, files in os.walk('.', topdown=False):
    try:
        os.rmdir(dirpath)
    except OSError as ex:
        pass
復制代碼

這將遍歷目錄樹并嘗試洗掉它找到的每個目錄, 如果目錄不為空,則引發OSError并跳過該目錄, 下表列出了本節中涉及的功能:

函式描述
os.remove() 洗掉單個檔案,不能洗掉目錄
os.unlink() 和os.remove()一樣,職能洗掉單個檔案
pathlib.Path.unlink() 洗掉單個檔案,不能洗掉目錄
os.rmdir() 洗掉一個空目錄
pathlib.Path.rmdir() 洗掉一個空目錄
shutil.rmtree() 洗掉完整的目錄樹,可用于洗掉非空目錄

復制、移動和重命名檔案和目錄

Python附帶了 shutil 模塊, shutil 是shell實用程式的縮寫, 它為檔案提供了許多高級操作,來支持檔案和目錄的復制,歸檔和洗掉, 在本節中,你將學習如何移動和復制檔案和目錄,

復制檔案

shutil 提供了一些復制檔案的函式, 最常用的函式是 shutil.copy() 和 shutil.copy2() , 使用shutil.copy() 將檔案從一個位置復制到另一個位置,請執行以下操作:

import shutil

src = https://www.cnblogs.com/chengxuyuanaa/p/'path/to/file.txt'
dst = 'path/to/dest_dir'
shutil.copy(src, dst)
復制代碼

shutil.copy() 與基于UNIX的系統中的 cp 命令相當, shutil.copy(src,dst) 會將檔案 src 復制到 dst 中指定的位置, 如果 dst 是檔案,則該檔案的內容將替換為 src 的內容, 如果 dst 是目錄,則 src 將被復制到該目錄中, shutil.copy() 僅復制檔案的內容和檔案的權限, 其他元資料(如檔案的創建和修改時間)不會保留,

要在復制時保留所有檔案元資料,請使用 shutil.copy2() :

import shutil

src = https://www.cnblogs.com/chengxuyuanaa/p/'path/to/file.txt'
dst = 'path/to/dest_dir'
shutil.copy2(src, dst)
復制代碼

使用 .copy2() 保留有關檔案的詳細資訊,例如上次訪問時間,權限位,上次修改時間和標志,

復制目錄

雖然 shutil.copy() 只復制單個檔案,但 shutil.copytree() 將復制整個目錄及其中包含的所有內容, shutil.copytree(src,dest) 接收兩個引數:源目錄和將檔案和檔案夾復制到的目標目錄,

以下是如何將一個檔案夾的內容復制到其他位置的示例:

import shutil
dst = shutil.copytree('data_1', 'data1_backup')
print(dst)  # data1_backup
復制代碼

在此示例中,.copytree() 將 data_1 的內容復制到新位置 data1_backup 并回傳目標目錄, 目標目錄不能是已存在的, 它將被創建而不帶有其父目錄, shutil.copytree() 是備份檔案的一個好方法,

移動檔案和目錄

要將檔案或目錄移動到其他位置,請使用 shutil.move(src,dst) ,

src 是要移動的檔案或目錄,dst 是目標:

import shutil
dst = shutil.move('dir_1/', 'backup/')
print(dst)  # 'backup'
復制代碼

如果 backup/ 存在,則 shutil.move('dir_1/','backup/') 將 dir_1/ 移動到 backup/ , 如果 backup/ 不存在,則 dir_1/ 將重命名為 backup ,

重命名檔案和目錄

Python包含用于重命名檔案和目錄的 os.rename(src,dst)

import os
os.rename('first.zip', 'first_01.zip')
復制代碼

上面的行將 first.zip 重命名為 first_01.zip , 如果目標路徑指向目錄,則會拋出 OSError ,

重命名檔案或目錄的另一種方法是使用 pathlib 模塊中的 rename()

from pathlib import Path
data_file = Path('data_01.txt')
data_file.rename('data.txt')
復制代碼

要使用 pathlib 重命名檔案,首先要創建一個 pathlib.Path() 物件,該物件包含要替換的檔案的路徑, 下一步是在路徑物件上呼叫 rename() 并傳入你要重命名的檔案或目錄的新名稱,


歸檔

歸檔是將多個檔案打包成一個檔案的便捷方式, 兩種最常見的存檔型別是ZIP和TAR, 你撰寫的Python程式可以創建存檔檔案,讀取存檔檔案和從存檔檔案中提取資料, 你將在本節中學習如何讀取和寫入兩種壓縮格式,

讀取ZIP檔案

zipfile 模塊是一個底層模塊,是Python標準庫的一部分, zipfile 具有可以輕松打開和提取ZIP檔案的函式, 要讀取ZIP檔案的內容,首先要做的是創建一個 ZipFile 物件,ZipFile 物件類似于使用 open() 創建的檔案物件,ZipFile 也是一個背景關系管理器,因此支持with陳述句:

import zipfile

with zipfile.ZipFile('data.zip', 'r') as zipobj:
    pass
復制代碼

這里創建一個 ZipFile 物件,傳入ZIP檔案的名稱并以讀取模式下打開, 打開ZIP檔案后,可以通過 zipfile 模塊提供的函式訪問有關存檔檔案的資訊, 上面示例中的 data.zip 存檔是從名為 data 的目錄創建的,該目錄包含總共5個檔案和1個子目錄:

.
|
├── sub_dir/
|   ├── bar.py
|   └── foo.py
|
├── file1.py
├── file2.py
└── file3.py
復制代碼

要獲取存檔檔案中的檔案串列,請在 ZipFile 物件上呼叫 namelist() :

import zipfile

with zipfile.ZipFile('data.zip', 'r') as zipobj:
    zipobj.namelist()
復制代碼

這會生成一個檔案串列:

['file1.py', 'file2.py', 'file3.py', 'sub_dir/', 'sub_dir/bar.py', 'sub_dir/foo.py']
復制代碼

.namelist() 回傳存檔檔案中檔案和目錄的名稱串列,要檢索有關存檔檔案中檔案的資訊,使用 .getinfo() :

import zipfile

with zipfile.ZipFile('data.zip', 'r') as zipobj:
    bar_info = zipobj.getinfo('sub_dir/bar.py')
    print(bar_info.file_size)
復制代碼

這將輸出:

15277
復制代碼

.getinfo() 回傳一個 ZipInfo 物件,該物件存盤有關存檔檔案的單個成員的資訊, 要獲取有關存檔檔案中檔案的資訊,請將其路徑作為引數傳遞給 .getinfo() , 使用 getinfo() ,你可以檢索有關存檔檔案成員的資訊,例如上次修改檔案的日期,壓縮大小及其完整檔案名, 訪問 .file_size 將以位元組為單位檢索檔案的原始大小,

以下示例說明如何在Python REPL中檢索有關已歸檔檔案的更多詳細資訊, 假設已匯入 zipfile 模塊,bar_info 與在前面的示例中創建的物件相同:

>>> bar_info.date_time
(2018, 10, 7, 23, 30, 10)
>>> bar_info.compress_size
2856
>>> bar_info.filename
'sub_dir/bar.py'
復制代碼

bar_info 包含有關 bar.py 的詳細資訊,例如壓縮的大小及其完整路徑,

第一行顯示了如何檢索檔案的上次修改日期, 下一行顯示了如何在歸檔后獲取檔案的大小, 最后一行顯示了存檔檔案中 bar.py 的完整路徑,

ZipFile 支持背景關系管理器協議,這就是你可以將它與with陳述句一起使用的原因, 操作完成后會自動關閉 ZipFile 物件, 嘗試從已關閉的 ZipFile 物件中打開或提取檔案將導致錯誤,

提取ZIP檔案

zipfile 模塊允許你通過 .extract() 和 .extractall() 從ZIP檔案中提取一個或多個檔案,

默認情況下,這些方法將檔案提取到當前目錄, 它們都采用可選的路徑引數,允許指定要將檔案提取到的其他指定目錄, 如果該目錄不存在,則會自動創建該目錄, 要從壓縮檔案中提取檔案,請執行以下操作:

>>> import zipfile
>>> import os

>>> os.listdir('.')
['data.zip']

>>> data_zip = zipfile.ZipFile('data.zip', 'r')

>>> # 提取單個檔案到當前目錄
>>> data_zip.extract('file1.py')
'/home/test/dir1/zip_extract/file1.py'

>>> os.listdir('.')
['file1.py', 'data.zip']

>>> # 提所有檔案到指定目錄
>>> data_zip.extractall(path='extract_dir/')

>>> os.listdir('.')
['file1.py', 'extract_dir', 'data.zip']

>>> os.listdir('extract_dir')
['file1.py', 'file3.py', 'file2.py', 'sub_dir']

>>> data_zip.close()
復制代碼

第三行代碼是對 os.listdir() 的呼叫,它顯示當前目錄只有一個檔案 data.zip ,

接下來,以讀取模式下打開 data.zip 并呼叫 .extract() 從中提取 file1.py , .extract() 回傳提取檔案的完整檔案路徑, 由于沒有指定路徑,.extract() 會將 file1.py 提取到當前目錄,

下一行列印一個目錄串列,顯示當前目錄現在包括除原始存檔檔案之外的存檔檔案, 之后顯示了如何將整個存檔提取到指定目錄中,.extractall() 創建 extract_dir 并將 data.zip 的內容提取到其中, 最后一行關閉ZIP存檔檔案,

從加密的檔案提取資料

zipfile 支持提取受密碼保護的ZIP, 要提取受密碼保護的ZIP檔案,請將密碼作為引數傳遞給 .extract() 或.extractall() 方法:

>>> import zipfile

>>> with zipfile.ZipFile('secret.zip', 'r') as pwd_zip:
...     # 從加密的檔案提取資料
...     pwd_zip.extractall(path='extract_dir', pwd='Quish3@o')
復制代碼

將以讀取模式打開 secret.zip 存檔, 密碼提供給 .extractall() ,并且壓縮檔案內容被提取到 extract_dir , 由于with陳述句,在完成提取后,存檔檔案會自動關閉,

創建新的存檔檔案

要創建新的ZIP存檔,請以寫入模式(w)打開 ZipFile 物件并添加要歸檔的檔案:

>>> import zipfile

>>> file_list = ['file1.py', 'sub_dir/', 'sub_dir/bar.py', 'sub_dir/foo.py']
>>> with zipfile.ZipFile('new.zip', 'w') as new_zip:
...     for name in file_list:
...         new_zip.write(name)
復制代碼

在該示例中,new_zip 以寫入模式打開,file_list 中的每個檔案都添加到存檔檔案中, with陳述句結束后,將關閉 new_zip , 以寫入模式打開ZIP檔案會洗掉壓縮檔案的內容并創建新存檔檔案,

要將檔案添加到現有的存檔檔案,請以追加模式打開 ZipFile 物件,然后添加檔案:

>>> with zipfile.ZipFile('new.zip', 'a') as new_zip:
...     new_zip.write('data.txt')
...     new_zip.write('latin.txt')
復制代碼

這里打開在上一個示例中以追加模式創建的 new.zip 存檔, 在追加模式下打開 ZipFile 物件允許將新檔案添加到ZIP檔案而不洗掉其當前內容, 將檔案添加到ZIP檔案后,with陳述句將脫離背景關系并關閉ZIP檔案,

打開TAR存檔檔案

TAR檔案是像ZIP等未壓縮的檔案存檔, 它們可以使用 gzipbzip2 和 lzma 壓縮方法進行壓縮, TarFile 類允許讀取和寫入TAR存檔,

下面是從存檔中讀取:

import tarfile

with tarfile.open('example.tar', 'r') as tar_file:
    print(tar_file.getnames())
復制代碼

tarfile 物件像大多數類似檔案的物件一樣打開, 它們有一個 open() 函式,它采用一種模式來確定檔案的打開方式,

使用“r”,“w”或“a”模式分別打開未壓縮的TAR檔案以進行讀取,寫入和追加, 要打開壓縮的TAR檔案,請將模式引數傳遞給 tarfile.open(),其格式為 filemode [:compression] , 下表列出了可以打開TAR檔案的可能模式:

模式行為
r 以無壓縮的讀取模式打開存檔
r:gz 以gzip壓縮的讀取模式打開存檔
r:bz2 以bzip2壓縮的讀取模式打開存檔
w 以無壓縮的寫入模式打開存檔
w:gz 以gzip壓縮的寫入模式打開存檔
w:xz 以lzma壓縮的寫入模式打開存檔
a 以無壓縮的追加模式打開存檔

.open() 默認為'r'模式, 要讀取未壓縮的TAR檔案并檢索其中的檔案名,請使用 .getnames() :

>>> import tarfile

>>> tar = tarfile.open('example.tar', mode='r')
>>> tar.getnames()
['CONTRIBUTING.rst', 'README.md', 'app.py']
復制代碼

這以串列的方式回傳存檔中內容的名字,

注意:為了向你展示如何使用不同的tarfile物件方法,示例中的TAR檔案在互動式REPL會話中手動打開和關閉,

通過這種方式與TAR檔案互動,你可以查看運行每個命令的輸出, 通常,你可能希望使用背景關系管理器來打開類似檔案的物件,

此外可以使用特殊屬性訪問存檔中每個條目的元資料:

>>> for entry in tar.getmembers():
...     print(entry.name)
...     print(' Modified:', time.ctime(entry.mtime))
...     print(' Size    :', entry.size, 'bytes')
...     print()
CONTRIBUTING.rst
 Modified: Sat Nov  1 09:09:51 2018
 Size    : 402 bytes

README.md
 Modified: Sat Nov  3 07:29:40 2018
 Size    : 5426 bytes

app.py
 Modified: Sat Nov  3 07:29:13 2018
 Size    : 6218 bytes
復制代碼

在此示例中,回圈遍歷 .getmembers() 回傳的檔案串列,并列印出每個檔案的屬性,.getmembers() 回傳的物件具有可以通過編程方式訪問的屬性,例如歸檔中每個檔案的名稱,大小和上次修改時間, 在讀取或寫入存檔后,必須關閉它以釋放系統資源,

從TAR存檔中提取檔案

在本節中,你將學習如何使用以下方法從TAR存檔中提取檔案:

  • .extract()
  • .extractfile()
  • .extractall()

要從TAR存檔中提取單個檔案,請使用 extract() ,傳入檔案名:

>>> tar.extract('README.md')
>>> os.listdir('.')
['README.md', 'example.tar']
復制代碼

README.md 檔案從存檔中提取到檔案系統, 呼叫 os.listdir() 確認 README.md 檔案已成功提取到當前目錄中, 要從存檔中解壓碩訓提取所有內容,請使用 .extractall() :

>>> tar.extractall(path="extracted/")
復制代碼

.extractall() 有一個可選的 path 引數來指定解壓縮檔案的去向, 這里,存檔被提取到 extracted 目錄中, 以下命令顯示已成功提取存檔:

$ ls
example.tar  extracted  README.md

$ tree
.
├── example.tar
├── extracted
|   ├── app.py
|   ├── CONTRIBUTING.rst
|   └── README.md
└── README.md

1 directory, 5 files

$ ls extracted/
app.py  CONTRIBUTING.rst  README.md
復制代碼

要提取檔案物件以進行讀取或寫入,請使用 .extractfile() ,它接收 檔案名或 TarInfo 物件作為引數, .extractfile() 回傳一個可以讀取和使用的類檔案物件:

>>> f = tar.extractfile('app.py')
>>> f.read()
>>> tar.close()
復制代碼

打開的存檔應在讀取或寫入后始終關閉, 要關閉存檔,請在存檔檔案句柄上呼叫 .close() ,或在創建 tarfile物件時使用with陳述句,以便在完成后自動關閉存檔, 這將釋放系統資源,并將你對存檔所做的任何更改寫入檔案系統,

創建新的TAR存檔

創建新的TAR存檔,你可以這樣操作:

>>> import tarfile

>>> file_list = ['app.py', 'config.py', 'CONTRIBUTORS.md', 'tests.py']
>>> with tarfile.open('packages.tar', mode='w') as tar:
...     for file in file_list:
...         tar.add(file)

>>> # Read the contents of the newly created archive
>>> with tarfile.open('package.tar', mode='r') as t:
...     for member in t.getmembers():
...         print(member.name)
app.py
config.py
CONTRIBUTORS.md
tests.py
復制代碼

首先,你要創建要添加到存檔的檔案串列,這樣你就不必手動添加每個檔案,

下一行使用with光線文管理器在寫入模式下打開名為 packages.tar 的新存檔, 以寫入模式('w')打開存檔使你可以將新檔案寫入存檔, 將洗掉存檔中的所有現有檔案,并創建新存檔,

創建并填充存檔后,with背景關系管理器會自動關閉它并將其保存到檔案系統, 最后三行打開剛剛創建的存檔,并列印出其中包含的檔案的名稱,

要將新檔案添加到現有存檔,請以追加模式('a')打開存檔:

>>> with tarfile.open('package.tar', mode='a') as tar:
...     tar.add('foo.bar')

>>> with tarfile.open('package.tar', mode='r') as tar:
...     for member in tar.getmembers():
...         print(member.name)
app.py
config.py
CONTRIBUTORS.md
tests.py
foo.bar
復制代碼

在追加模式下打開存檔允許你向其添加新檔案而不洗掉其中已存在的檔案,

使用壓縮存檔

tarfile 可以讀取和寫入使用 gzipbzip2 和 lzma 壓縮的TAR存檔檔案, 要讀取或寫入壓縮存檔,請使用tarfile.open() ,為壓縮型別傳遞適當的模式,

例如,要讀取或寫入使用 gzip 壓縮的TAR存檔的資料,請分別使用 'r:gz' 或 'w:gz' 模式:

>>> files = ['app.py', 'config.py', 'tests.py']
>>> with tarfile.open('packages.tar.gz', mode='w:gz') as tar:
...     tar.add('app.py')
...     tar.add('config.py')
...     tar.add('tests.py')

>>> with tarfile.open('packages.tar.gz', mode='r:gz') as t:
...     for member in t.getmembers():
...         print(member.name)
app.py
config.py
tests.py
復制代碼

'w:gz' 以寫模式模式打開 gzip 壓縮的存檔,'r:gz' 以讀模式打開 gzip 壓縮的存檔, 無法在追加模式下打開壓縮存檔, 要將檔案添加到壓縮存檔,你必須創建新存檔,


一個更簡單的方式創建存檔

Python標準庫還支持使用 shutil 模塊中的高級方法創建TAR和ZIP存檔, shutil 中的歸檔實用工具允許你創建,讀取和提取ZIP和TAR歸檔, 這些實用工具依賴于較底層的 tarfile 和 zipfile 模塊,

使用 shutil.make_archive() 創建存檔

shutil.make_archive() 至少接收兩個引數:歸檔的名稱和歸檔格式,

默認情況下,它將當前目錄中的所有檔案壓縮為 format 引數中指定的歸檔格式, 你可以傳入可選的 root_dir 引數來壓縮不同目錄中的檔案, .make_archive() 支持 zip ,tar ,bztar 和 gztar 存檔格式,

以下是使用 shutil 創建TAR存檔的方法:

import shutil

# shutil.make_archive(base_name, format, root_dir)
shutil.make_archive('data/backup', 'tar', 'data/')
復制代碼

這將復制 data / 中的所有內容,并在檔案系統中創建名為 backup.tar 的存檔并回傳其名稱, 要提取存檔,請呼叫 .unpack_archive() :

shutil.unpack_archive('backup.tar', 'extract_dir/')
復制代碼

呼叫 .unpack_archive() 并傳入存檔名稱和目標目錄,將 backup.tar 的內容提取到 extract_dir/ 中, ZIP存檔可以以相同的方式創建和提取,


讀取多個檔案

Python支持通過 fileinput 模塊從多個輸入流或檔案串列中讀取資料, 此模塊允許你快速輕松地回圈遍歷一個或多個文本檔案的內容, 以下是使用 fileinput 的典型方法:

import fileinput
for line in fileinput.input()
    process(line)
復制代碼

fileinput 默認從傳遞給 sys.argv 的命令列引數獲取其輸入,

使用 fileinput 回圈遍歷多個檔案

讓我們使用 fileinput 構建一個普通的UNIX工具 cat 的原始版本, cat 工具按順序讀取檔案,將它們寫入標準輸出, 當在命令列引數中給出多個檔案時,cat 將連接文本檔案并在終端中顯示結果:

# File: fileinput-example.py
import fileinput
import sys

files = fileinput.input()
for line in files:
    if fileinput.isfirstline():
        print(f'\n--- Reading {fileinput.filename()} ---')
    print(' -> ' + line, end='')
print()
復制代碼

在當前目錄中有兩個文本檔案,運行此命令會產生以下輸出:

$ python3 fileinput-example.py bacon.txt cupcake.txt
--- Reading bacon.txt ---
 -> Spicy jalapeno bacon ipsum dolor amet in in aute est qui enim aliquip,
 -> irure cillum drumstick elit.
 -> Doner jowl shank ea exercitation landjaeger incididunt ut porchetta.
 -> Tenderloin bacon aliquip cupidatat chicken chuck quis anim et swine.
 -> Tri-tip doner kevin cillum ham veniam cow hamburger.
 -> Turkey pork loin cupidatat filet mignon capicola brisket cupim ad in.
 -> Ball tip dolor do magna laboris nisi pancetta nostrud doner.

--- Reading cupcake.txt ---
 -> Cupcake ipsum dolor sit amet candy I love cheesecake fruitcake.
 -> Topping muffin cotton candy.
 -> Gummies macaroon jujubes jelly beans marzipan.
復制代碼

fileinput 允許你檢索有關每一行的更多資訊,例如它是否是第一行(.isfirstline()),行號(.lineno())和檔案名(.filename()), 你可以在 這里 讀更多關于它的內容,


總結

你現在知道如何使用Python對檔案和檔案組執行最常見的操作, 你已經了解使用不同的內置模塊來讀取,查找和操作檔案,

你現在可以用Python來實作:

  • 獲取目錄內容和檔案屬性
  • 創建目錄和目錄樹
  • 使用匹配模式匹配檔案名
  • 創建臨時檔案和目錄
  • 移動,重命名,復制和洗掉檔案或目錄
  • 從不同型別的存檔檔案中讀取和提取資料
  • 使用 fileinput 同時讀取多個檔案

    最后注意:不管你是為了Python就業還是興趣愛好,記住:專案開發經驗永遠是核心,如果如果你沒有2020最新python入門到高級實戰視頻教程,可以去小編的Python交流.裙 :七衣衣九七七巴而五(數字的諧音)轉換下可以找到了,里面很多新python教程專案,還可以跟老司機交流討教!
    本文的文字及圖片來源于網路加上自己的想法,僅供學習、交流使用,不具有任何商業用途,著作權歸原作者所有,如有問題請及時聯系我們以作處理,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/157167.html

標籤:Python

上一篇:Python單例模式最佳實戰【入門必學】

下一篇:新手求助,tomcat一直啟動失敗

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more