Python文件操作,看這篇就足夠
本文為譯文,原文鏈接 working-with-files-in-python
本人博客: 編程禪師
Python中有幾個內(nèi)置模塊和方法來處理文件。這些方法被分割到例如os, os.path , shutil 和 pathlib 等等幾個模塊中。文章將列舉Python中對文件最常用的操作和方法。
在這篇文章中,你將學(xué)習(xí)如何:
- 獲取文件屬性
- 創(chuàng)建目錄
- 文件名模式匹配
- 遍歷目錄樹
- 創(chuàng)建臨時文件和目錄
- 刪除文件和目錄
- 復(fù)制、移動和重命名文件和目錄
- 創(chuàng)建和解壓ZIP和TAR檔案
- 使用
fileinput模塊打開多個文件
Python中文件數(shù)據(jù)的讀和寫
使用Python對文件進行讀和寫是十分簡單的。為此,你首先必須使用合適的模式打開文件。這里有一個如何打開文本文件并讀取其內(nèi)容的例子。
with open('data.txt', 'r') as f:
data = f.read()
print('context: {}'.format(data))
open() 接收一個文件名和一個模式作為它的參數(shù),r 表示以只讀模式打開文件。想要往文件中寫數(shù)據(jù)的話,則用w 作為參數(shù)。
with open('data.txt', 'w') as f:
data = 'some data to be written to the file'
f.write(data)
在上述例子中,open()打開用于讀取或?qū)懭氲奈募⒎祷匚募浔?本例子中的 f ),該句柄提供了可用于讀取或?qū)懭胛募?shù)據(jù)的方法。閱讀 Working With File I/O in Python 獲取更多關(guān)于如何讀寫文件的信息。
獲取目錄列表
假設(shè)你當(dāng)前的工作目錄有一個叫 my_directory 的子目錄,該目錄包含如下內(nèi)容:
.
├── 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內(nèi)置的 os 模塊有很多有用的方法能被用來列出目錄內(nèi)容和過濾結(jié)果。為了獲取文件系統(tǒng)中特定目錄的所有文件和文件夾列表,可以在遺留版本的Python中使用 os.listdir() 或 在Python 3.x 中使用 os.scandir() 。 如果你還想獲取文件和目錄屬性(如文件大小和修改日期),那么 os.scandir() 則是首選的方法。
使用遺留版本的Python獲取目錄列表
import os
entries = os.listdir('my_directory')
os.listdir() 返回一個Python列表,其中包含path參數(shù)所指目錄的文件和子目錄的名稱。
['file1.py', 'file2.csv', 'file3.txt', 'sub_dir', 'sub_dir_b', 'sub_dir_c']
目錄列表現(xiàn)在看上去不容易閱讀,對 os.listdir() 的調(diào)用結(jié)果使用循環(huán)打印有助于查看。
for entry in entries:
print(entry)
"""
file1.py
file2.csv
file3.txt
sub_dir
sub_dir_b
sub_dir_c
"""
使用現(xiàn)代版本的Python獲取目錄列表
在現(xiàn)代Python版本中,可以使用 os.scandir() 和 pathlib.Path 來替代 os.listdir() 。
os.scandir() 在Python 3.5 中被引用,其文檔為 PEP 471 。
os.scandir() 調(diào)用時返回一個迭代器而不是一個列表。
import os
entries = os.scandir('my_directory')
print(entries)
# <posix.ScandirIterator at 0x105b4d4b0>
ScandirIterator 指向了當(dāng)前目錄中的所有條目。你可以遍歷迭代器的內(nèi)容,并打印文件名。
import os
with os.scandir('my_directory') as entries:
for entry in entries:
print(entry.name)
這里 os.scandir() 和with語句一起使用,因為它支持上下文管理協(xié)議。使用上下文管理器關(guān)閉迭代器并在迭代器耗盡后自動釋放獲取的資源。在 my_directory 打印文件名的結(jié)果就和在 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 對象,這取決于操作系統(tǒng)。
pathlib.Path() 對象有一個 .iterdir() 的方法用于創(chuàng)建一個迭代器包含該目錄下所有文件和目錄。由 .iterdir() 生成的每個條目都包含文件或目錄的信息,例如其名稱和文件屬性。pathlib 在Python3.4時被第一次引入,并且是對Python一個很好的加強,它為文件系統(tǒng)提供了面向?qū)ο蟮慕涌凇?/p>
在上面的例子中,你調(diào)用 pathlib.Path() 并傳入了一個路徑參數(shù)。然后調(diào)用 .iterdir() 來獲取 my_directory 下的所有文件和目錄列表。
pathlib 提供了一組類,以簡單并且面向?qū)ο蟮姆绞教峁┝寺窂缴系拇蠖鄶?shù)常見的操作。使用 pathlib 比起使用 os 中的函數(shù)更加有效。和 os 相比,使用 pathlib 的另一個好處是減少了操作文件系統(tǒng)路徑所導(dǎo)入包或模塊的數(shù)量。想要了解更多信息,可以閱讀 Python 3’s pathlib Module: Taming the File System 。
運行上述代碼會得到如下結(jié)果:
file1.py
file2.csv
file3.txt
sub_dir
sub_dir_b
sub_dir_c
使用 pathlib.Path() 或 os.scandir() 來替代 os.listdir() 是獲取目錄列表的首選方法,尤其是當(dāng)你需要獲取文件類型和文件屬性信息的時候。pathlib.Path() 提供了在 os 和 shutil 中大部分處理文件和路徑的功能,并且它的方法比這些模塊更加有效。我們將討論如何快速的獲取文件屬性。
| 函數(shù) | 描述 |
|---|---|
| os.listdir() | 以列表的方式返回目錄中所有的文件和文件夾 |
| os.scandir() | 返回一個迭代器包含目錄中所有的對象,對象包含文件屬性信息 |
| pathlib.Path().iterdir() | 返回一個迭代器包含目錄中所有的對象,對象包含文件屬性信息 |
這些函數(shù)返回目錄中所有內(nèi)容的列表,包括子目錄。這可能并總是你一直想要的結(jié)果,下一節(jié)將向你展示如何從目錄列表中過濾結(jié)果。
列出目錄中的所有文件
這節(jié)將向你展示如何使用 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)
在這里調(diào)用 os.listdir() 返回指定路徑中所有內(nèi)容的列表,接著使用 os.path.isfile() 過濾列表讓其只顯示文件類型而非目錄類型。代碼執(zhí)行結(jié)果如下:
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 的每一項調(diào)用 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() 產(chǎn)生的每一項調(diào)用 .is_file() 。產(chǎn)生的輸出結(jié)果和上面相同:
file1.py
file3.txt
file2.csv
如果將for循環(huán)和if語句組合成單個生成器表達式,則上述的代碼可以更加簡潔。關(guān)于生成器表達式,推薦一篇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)
上述代碼的執(zhí)行結(jié)果和之前相同。本節(jié)展示使用 os.scandir() 和 pathlib.Path() 過濾文件或目錄比使用 os.listdir() 和 os.path 更直觀,代碼看起來更簡潔。
列出子目錄
如果要列出子目錄而不是文件,請使用下面的方法?,F(xiàn)在展示如何使用 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)
當(dāng)你多次調(diào)用 os.path,join() 時,以這種方式操作文件系統(tǒng)就會變得很笨重。在我電腦上運行此代碼會產(chǎn)生以下輸出:
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() 返回的每一項上調(diào)用 .is_dir() 。如果這項是目錄,則 is_dir() 返回 True,并打印出目錄的名稱。輸出結(jié)果和上面相同:
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() 迭代器返回的每一項上調(diào)用 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() 方法能獲取關(guān)于它指向文件或目錄的信息。.stat() 提供了例如文件大小和最后修改時間的信息。在上面的示例中,代碼打印了 st_time 屬性,該屬性是上次修改文件內(nèi)容的時間。
pathlib 模塊具有相應(yīng)的方法,用于獲取相同結(jié)果的文件信息:
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
"""
在上面的例子中,循環(huán) .iterdir() 返回的迭代器并通過對其中每一項調(diào)用 .stat() 來獲取文件屬性。st_mtime 屬性是一個浮點類型的值,表示的是時間戳。為了讓 st_time 返回的值更容易閱讀,你可以編寫一個輔助函數(shù)將其轉(zhuǎn)換為一個 datetime 對象:
import datetime
from pathlib import Path
def timestamp2datetime(timestamp, convert_to_local=True, utc=8, is_remove_ms=True)
"""
轉(zhuǎn)換 UNIX 時間戳為 datetime對象
:param timestamp: 時間戳
:param convert_to_local: 是否轉(zhuǎn)為本地時間
:param utc: 時區(qū)信息,中國為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 中文件的列表以及它們的屬性,然后調(diào)用 convert_date() 來轉(zhuǎn)換文件最后修改時間讓其以一種人類可讀的方式顯示。convert_date() 使用 .strftime() 將datetime類型轉(zhuǎn)換為字符串。
上述代碼的輸出結(jié)果:
file3.txt 上次修改時間為 2019-01-24 09:04:39
file2.csv 上次修改時間為 2019-01-24 09:04:39
file1.py 上次修改時間為 2019-01-24 09:04:39
將日期和時間轉(zhuǎn)換為字符串的語法可能會讓你感到混亂。如果要了解更多的信息,請查詢相關(guān)的官方文檔 。另一個方式則是閱讀 http://strftime.org 。
創(chuàng)建目錄
你編寫的程序遲早需要創(chuàng)建目錄以便在其中存儲數(shù)據(jù)。 os 和 pathlib 包含了創(chuàng)建目錄的函數(shù)。我們將會考慮如下方法:
| 方法 | 描述 |
|---|---|
| os.mkdir() | 創(chuàng)建單個子目錄 |
| os.makedirs() | 創(chuàng)建多個目錄,包括中間目錄 |
| Pathlib.Path.mkdir() | 創(chuàng)建單個或多個目錄 |
創(chuàng)建單個目錄
要創(chuàng)建單個目錄,把目錄路徑作為參數(shù)傳給 os.mkdir() :
import os
os.mkdir('example_directory')
如果該目錄已經(jīng)存在,os.mkdir() 將拋出 FileExistsError 異常?;蛘?,你也可以使用 pathlib 來創(chuàng)建目錄:
from pathlib import Path
p = Path('example_directory')
p.mkdir()
如果路徑已經(jīng)存在,mkdir() 會拋出 FileExistsError 異常:
FileExistsError: [Errno 17] File exists: 'example_directory'
為了避免像這樣的錯誤拋出, 當(dāng)發(fā)生錯誤時捕獲錯誤并讓你的用戶知道:
from pathlib import Path
p = Path('example_directory')
try:
p.mkdir()
except FileExistsError as e:
print(e)
或者,你可以給 .mkdir() 傳入 exist_ok=True 參數(shù)來忽略 FileExistsError 異常:
from pathlib import Path
p = Path('example_directory')
p.mkdir(exist_ok=True)
如果目錄已存在,則不會引起錯誤。
創(chuàng)建多個目錄
os.makedirs() 和 os.mkdir() 類似。兩者之間的區(qū)別在于,os.makedirs() 不僅可以創(chuàng)建單獨的目錄,還可以遞歸的創(chuàng)建目錄樹。換句話說,它可以創(chuàng)建任何必要的中間文件夾,來確保存在完整的路徑。
os.makedirs() 和在bash中運行 mkdir -p 類似。例如,要創(chuàng)建一組目錄像 2018/10/05,你可以像下面那樣操作:
import os
os.makedirs('2018/10/05', mode=0o770)
上述代碼創(chuàng)建了 2018/10/05 的目錄結(jié)構(gòu)并為所有者和組用戶提供讀、寫和執(zhí)行權(quán)限。默認(rèn)的模式為 0o777 ,增加了其他用戶組的權(quán)限。有關(guān)文件權(quán)限以及模式的應(yīng)用方式的更多詳細信息,請參考 文檔 。
運行 tree 命令確認(rèn)我們應(yīng)用的權(quán)限:
$ tree -p -i .
.
[drwxrwx---] 2018
[drwxrwx---] 10
[drwxrwx---] 05
上述代碼打印出當(dāng)前目錄的目錄樹。 tree 通常被用來以樹形結(jié)構(gòu)列出目錄的內(nèi)容。傳入 -p 和 -i 參數(shù)則會以垂直列表打印出目錄名稱以及其文件權(quán)限信息。-p 用于輸出文件權(quán)限,-i 則用于讓 tree 命令產(chǎn)生一個沒有縮進線的垂直列表。
正如你所看到的,所有的目錄都擁有 770 權(quán)限。另一個方式創(chuàng)建多個目錄是使用 pathlib.Path 的 .mkdir() :
from pathlib import Path
p = Path('2018/10/05')
p.mkdir(parents=True, exist_ok=True)
通過給 Path.mkdir() 傳遞 parents=True 關(guān)鍵字參數(shù)使它創(chuàng)建 05 目錄和使其路徑有效的所有父級目錄。
在默認(rèn)情況下,os.makedirs() 和 pathlib.Path.mkdir() 會在目標(biāo)目錄存在的時候拋出 OSError 。通過每次調(diào)用函數(shù)時傳遞 exist_ok=True 作為關(guān)鍵字參數(shù)則可以覆蓋此行為(從Python3.2開始)。
運行上述代碼會得到像下面的結(jié)構(gòu):
└── 2018
└── 10
└── 05
我更喜歡在創(chuàng)建目錄時使用 pathlib ,因為我可以使用相同的函數(shù)方法來創(chuàng)建一個或多個目錄。
文件名模式匹配
使用上述方法之一獲取目錄中的文件列表后,你可能希望搜索和特定的模式匹配的文件。
下面這些是你可以使用的方法和函數(shù):
-
endswith()和startswith()字符串方法 fnmatch.fnmatch()glob.glob()pathlib.Path.glob()
這些方法和函數(shù)是下面要討論的。本小節(jié)的示例將在名為 some_directory 的目錄下執(zhí)行,該目錄具有以下的結(jié)構(gòu):
.
├── 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,你可以使用以下的命令創(chuàng)建上述目錄結(jié)構(gòu):
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
這將會創(chuàng)建 some_directory 目錄并進入它,接著創(chuàng)建 sub_dir 。下一行在 sub_dir 創(chuàng)建 file1.py 和 file2.py ,最后一行使用擴展創(chuàng)建其它所有文件。想要學(xué)習(xí)更多關(guān)于shell擴展,請閱讀 這里 。
使用字符串方法
Python有幾個內(nèi)置 修改和操作字符串 的方法。當(dāng)在匹配文件名時,其中的兩個方法 .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 有對于模式匹配有更先進的函數(shù)和方法。我們將考慮使用 fnmatch.fnmatch() ,這是一個支持使用 * 和 ? 等通配符的函數(shù)。例如,使用 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 的文件執(zhí)行通配符搜索。
更先進的模式匹配
假設(shè)你想要查找符合特定掉件的 .txt 文件。例如,你可能指向找到包含單次 data 的 .txt文件,一組下劃線之間的數(shù)字,以及文件名中包含單詞 backup 。就類似于 data_01_backup, data_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和相關(guān)系統(tǒng)在文件列表中使用通配符像 ? 和 * 表示全匹配。
例如,在UNIX shell中使用 mv *.py python_files 移動所有 .py 擴展名 的文件從當(dāng)前目錄到 python_files 。這 * 是一個通配符表示任意數(shù)量的字符,*.py 是一個全模式。Windows操作系統(tǒng)中不提供此shell功能。但 glob 模塊在Python中添加了此功能,使得Windows程序可以使用這個特性。
這里有一個使用 glob 模塊在當(dāng)前目錄下查詢所有Python代碼文件:
import glob
print(glob.glob('*.py'))
glob.glob('*.py') 搜索當(dāng)前目錄中具有 .py 擴展名的文件,并且將它們以列表的形式返回。 glob 還支持 shell 樣式的通配符來進行匹配 :
import glob
for name in glob.glob('*[0-9]*.txt'):
print(name)
這將找到所有文件名中包含數(shù)字的文本文件(.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() 在當(dāng)前目錄和子目錄中搜索所有的 .py 文件。傳遞 recursive=True 作為 .iglob() 的參數(shù)使其搜索當(dāng)前目錄和子目錄中的 .py 文件。glob.glob() 和 glob.iglob() 不同之處在于,iglob() 返回一個迭代器而不是一個列表。
運行上述代碼會得到以下結(jié)果:
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)
調(diào)用 p.glob('*.p*') 會返回一個指向當(dāng)前目錄中所有擴展名以字母 p 開頭的文件的生成器對象。
Path.glob() 和上面討論過的 os.glob() 類似。正如你看到的, pathlib 混合了許多 os , os.path 和 glob 模塊的最佳特性到一個模塊中,這使得使用起來很方便。
回顧一下,這是我們在本節(jié)中介紹的功能表:
| 函數(shù) | 描述 |
|---|---|
| startswith() | 測試一個字符串是否以一個特定的模式開始,返回 True 或 False |
| endswith() | 測試一個字符串是否以一個特定的模式結(jié)束,返回 True 或 False |
| fnmatch.fnmatch(filename, pattern) | 測試文件名是否匹配這個模式,返回 True 或 False |
| glob.glob() | 返回一個匹配該模式的文件名列表 |
| pathlib.Path.glob() | 返回一個匹配該模式的生成器對象 |
遍歷目錄和處理文件
一個常見的編程任務(wù)是遍歷目錄樹并處理目錄樹中的文件。讓我們來探討一下如何使用內(nèi)置的Python函數(shù) os.walk() 來實現(xiàn)這一功能。os.walk() 用于通過從上到下或從下到上遍歷樹來生成目錄樹中的文件名。處于本節(jié)的目的,我們想操作以下的目錄樹:
├── folder_1
│ ├── file1.py
│ ├── file2.py
│ └── file3.py
├── folder_2
│ ├── file4.py
│ ├── file5.py
│ └── file6.py
├── test1.txt
└── test2.txt
以下是一個示例,演示如何使用 os.walk() 列出目錄樹中的所有文件和目錄。
os.walk() 默認(rèn)是從上到下遍歷目錄:
import os
for dirpath, dirname, files in os.walk('.'):
print(f'Found directory: {dirpath}')
for file_name in files:
print(file_name)
os.walk() 在每個循環(huán)中返回三個值:
- 當(dāng)前文件夾的名稱
- 當(dāng)前文件夾中子文件夾的列表
- 當(dāng)前文件夾中文件的列表
在每次迭代中,會打印出它找到的子目錄和文件的名稱:
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 關(guān)鍵字參數(shù)傳遞給 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 參數(shù)將使 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
如你看見的,程序在列出根目錄的內(nèi)容之前列出子目錄的內(nèi)容。 這在在你想要遞歸刪除文件和目錄的情況下非常有用。 你將在以下部分中學(xué)習(xí)如何執(zhí)行此操作。 默認(rèn)情況下,os.walk 不會訪問通過軟連接創(chuàng)建的目錄。 可以通過使用 followlinks = True 參數(shù)來覆蓋默認(rèn)行為。
創(chuàng)建臨時文件和目錄
Python提供了 tempfile 模塊來便捷的創(chuàng)建臨時文件和目錄。
tempfile 可以在你程序運行時打開并存儲臨時的數(shù)據(jù)在文件或目錄中。 tempfile 會在你程序停止運行后刪除這些臨時文件。
現(xiàn)在,讓我們看看如何創(chuàng)建一個臨時文件:
from tempfile import TemporaryFile
# 創(chuàng)建一個臨時文件并為其寫入一些數(shù)據(jù)
fp = TemporaryFile('w+t')
fp.write('Hello World!')
# 回到開始,從文件中讀取數(shù)據(jù)
fp.seek(0)
data = fp.read()
print(data)
# 關(guān)閉文件,之后他將會被刪除
fp.close()
第一步是從 tempfile 模塊導(dǎo)入 TemporaryFile 。 接下來,使用 TemporaryFile() 方法并傳入一個你想打開這個文件的模式來創(chuàng)建一個類似于對象的文件。這將創(chuàng)建并打開一個可用作臨時存儲區(qū)域的文件。
在上面的示例中,模式為 w + t,這使得 tempfile 在寫入模式下創(chuàng)建臨時文本文件。 沒有必要為臨時文件提供文件名,因為在腳本運行完畢后它將被銷毀。
寫入文件后,您可以從中讀取并在完成處理后將其關(guān)閉。 一旦文件關(guān)閉后,將從文件系統(tǒng)中刪除。 如果需要命名使用 tempfile 生成的臨時文件,請使用 tempfile.NamedTemporaryFile() 。
使用 tempfile 創(chuàng)建的臨時文件和目錄存儲在用于存儲臨時文件的特殊系統(tǒng)目錄中。 Python將在目錄列表搜索用戶可以在其中創(chuàng)建文件的目錄。
在Windows上,目錄按順序為 C:\TEMP,C:\TMP,\TEMP 和 \TMP。 在所有其他平臺上,目錄按順序為 / tmp,/var/tmp 和 /usr/tmp 。 如果上述目錄中都沒有,tempfile 將在當(dāng)前目錄中存儲臨時文件和目錄。
.TemporaryFile() 也是一個上下文管理器,因此它可以與with語句一起使用。 使用上下文管理器會在讀取文件后自動關(guān)閉和刪除文件:
with TemporaryFile('w+t') as fp:
fp.write('Hello universe!')
fp.seek(0)
fp.read()
# 臨時文件現(xiàn)在已經(jīng)被關(guān)閉和刪除
這將創(chuàng)建一個臨時文件并從中讀取數(shù)據(jù)。 一旦讀取文件的內(nèi)容,就會關(guān)閉臨時文件并從文件系統(tǒng)中刪除。
tempfile 也可用于創(chuàng)建臨時目錄。 讓我們看一下如何使用 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))
調(diào)用 tempfile.TemporaryDirectory() 會在文件系統(tǒng)中創(chuàng)建一個臨時目錄,并返回一個表示該目錄的對象。 在上面的示例中,使用上下文管理器創(chuàng)建目錄,目錄的名稱存儲在 tmpdir 變量中。 第三行打印出臨時目錄的名稱,os.path.exists(tmpdir) 來確認(rèn)目錄是否實際在文件系統(tǒng)中創(chuàng)建。
在上下文管理器退出上下文后,臨時目錄將被刪除,并且對 os.path.exists(tmpdir)的調(diào)用將返回False,這意味著該目錄已成功刪除。
刪除文件和目錄
您可以使用 os,shutil 和 pathlib 模塊中的方法刪除單個文件,目錄和整個目錄樹。 以下將介紹如何刪除你不再需要的文件和目錄。
Python中刪除文件
要刪除單個文件,請使用 pathlib.Path.unlink(),os.remove() 或 os.unlink()。
os.remove() 和 os.unlink() 在語義上是相同的。 要使用 os.remove()刪除文件,請執(zhí)行以下操作:
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)
在文件上調(diào)用 .unlink() 或 .remove() 會從文件系統(tǒng)中刪除該文件。 如果傳遞給它們的路徑指向目錄而不是文件,這兩個函數(shù)將拋出 OSError 。 為避免這種情況,可以檢查你要刪除的內(nèi)容是否是文件,并在確認(rèn)是文件時執(zhí)行刪除操作,或者可以使用異常處理來處理 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 是否實際上是一個文件。 如果是,則通過調(diào)用 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}')
這將創(chuàng)建一個名為 data_file 的 Path 對象,該對象指向一個文件。 在 data_file 上調(diào)用.unlink()將刪除 home / data.txt 。 如果 data_file 指向目錄,則引發(fā) IsADirectoryError 。 值得注意的是,上面的Python程序和運行它的用戶具有相同的權(quán)限。 如果用戶沒有刪除文件的權(quán)限,則會引發(fā) PermissionError 。
刪除目錄
標(biāo)準(zhǔn)庫提供了一下函數(shù)來刪除目錄:
- os.rmdir()
- pathlib.Path.rmdir()
- shutil.rmtree()
要刪除單個目錄或文件夾可以使用 os.rmdir() 或 pathlib.Path.rmdir() 。這兩個函數(shù)只在你刪除空目錄的時候有效。如果目錄不為空,則會拋出 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}')
現(xiàn)在,trash_dir 已經(jīng)通過 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}')
這里創(chuàng)建了一個 Path 對象指向要被刪除的目錄。如果目錄為空,調(diào)用 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}')
當(dāng)調(diào)用 shutil.rmtree() 時,trash_dir 中的所有內(nèi)容都將被刪除。 在某些情況下,你可能希望以遞歸方式刪除空文件夾。 你可以使用上面討論的方法之一結(jié)合 os.walk() 來完成此操作:
import os
for dirpath, dirnames, files in os.walk('.', topdown=False):
try:
os.rmdir(dirpath)
except OSError as ex:
pass
這將遍歷目錄樹并嘗試刪除它找到的每個目錄。 如果目錄不為空,則引發(fā)OSError并跳過該目錄。 下表列出了本節(jié)中涉及的功能:
| 函數(shù) | 描述 |
|---|---|
| os.remove() | 刪除單個文件,不能刪除目錄 |
| os.unlink() | 和os.remove()一樣,職能刪除單個文件 |
| pathlib.Path.unlink() | 刪除單個文件,不能刪除目錄 |
| os.rmdir() | 刪除一個空目錄 |
| pathlib.Path.rmdir() | 刪除一個空目錄 |
| shutil.rmtree() | 刪除完整的目錄樹,可用于刪除非空目錄 |
復(fù)制、移動和重命名文件和目錄
Python附帶了 shutil 模塊。 shutil 是shell實用程序的縮寫。 它為文件提供了許多高級操作,來支持文件和目錄的復(fù)制,歸檔和刪除。 在本節(jié)中,你將學(xué)習(xí)如何移動和復(fù)制文件和目錄。
復(fù)制文件
shutil 提供了一些復(fù)制文件的函數(shù)。 最常用的函數(shù)是 shutil.copy() 和 shutil.copy2() 。 使用shutil.copy() 將文件從一個位置復(fù)制到另一個位置,請執(zhí)行以下操作:
import shutil
src = 'path/to/file.txt'
dst = 'path/to/dest_dir'
shutil.copy(src, dst)
shutil.copy() 與基于UNIX的系統(tǒng)中的 cp 命令相當(dāng)。 shutil.copy(src,dst) 會將文件 src 復(fù)制到 dst 中指定的位置。 如果 dst 是文件,則該文件的內(nèi)容將替換為 src 的內(nèi)容。 如果 dst 是目錄,則 src 將被復(fù)制到該目錄中。 shutil.copy() 僅復(fù)制文件的內(nèi)容和文件的權(quán)限。 其他元數(shù)據(jù)(如文件的創(chuàng)建和修改時間)不會保留。
要在復(fù)制時保留所有文件元數(shù)據(jù),請使用 shutil.copy2() :
import shutil
src = 'path/to/file.txt'
dst = 'path/to/dest_dir'
shutil.copy2(src, dst)
使用 .copy2() 保留有關(guān)文件的詳細信息,例如上次訪問時間,權(quán)限位,上次修改時間和標(biāo)志。
復(fù)制目錄
雖然 shutil.copy() 只復(fù)制單個文件,但 shutil.copytree() 將復(fù)制整個目錄及其中包含的所有內(nèi)容。 shutil.copytree(src,dest) 接收兩個參數(shù):源目錄和將文件和文件夾復(fù)制到的目標(biāo)目錄。
以下是如何將一個文件夾的內(nèi)容復(fù)制到其他位置的示例:
import shutil
dst = shutil.copytree('data_1', 'data1_backup')
print(dst) # data1_backup
在此示例中,.copytree() 將 data_1 的內(nèi)容復(fù)制到新位置 data1_backup 并返回目標(biāo)目錄。 目標(biāo)目錄不能是已存在的。 它將被創(chuàng)建而不帶有其父目錄。 shutil.copytree() 是備份文件的一個好方法。
移動文件和目錄
要將文件或目錄移動到其他位置,請使用 shutil.move(src,dst) 。
src 是要移動的文件或目錄,dst 是目標(biāo):
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 。 如果目標(biāo)路徑指向目錄,則會拋出 OSError 。
重命名文件或目錄的另一種方法是使用 pathlib 模塊中的 rename():
from pathlib import Path
data_file = Path('data_01.txt')
data_file.rename('data.txt')
要使用 pathlib 重命名文件,首先要創(chuàng)建一個 pathlib.Path() 對象,該對象包含要替換的文件的路徑。 下一步是在路徑對象上調(diào)用 rename() 并傳入你要重命名的文件或目錄的新名稱。
歸檔
歸檔是將多個文件打包成一個文件的便捷方式。 兩種最常見的存檔類型是ZIP和TAR。 你編寫的Python程序可以創(chuàng)建存檔文件,讀取存檔文件和從存檔文件中提取數(shù)據(jù)。 你將在本節(jié)中學(xué)習(xí)如何讀取和寫入兩種壓縮格式。
讀取ZIP文件
zipfile 模塊是一個底層模塊,是Python標(biāo)準(zhǔn)庫的一部分。 zipfile 具有可以輕松打開和提取ZIP文件的函數(shù)。 要讀取ZIP文件的內(nèi)容,首先要做的是創(chuàng)建一個 ZipFile 對象。ZipFile 對象類似于使用 open() 創(chuàng)建的文件對象。ZipFile 也是一個上下文管理器,因此支持with語句:
import zipfile
with zipfile.ZipFile('data.zip', 'r') as zipobj:
pass
這里創(chuàng)建一個 ZipFile 對象,傳入ZIP文件的名稱并以讀取模式下打開。 打開ZIP文件后,可以通過 zipfile 模塊提供的函數(shù)訪問有關(guān)存檔文件的信息。 上面示例中的 data.zip 存檔是從名為 data 的目錄創(chuàng)建的,該目錄包含總共5個文件和1個子目錄:
.
|
├── sub_dir/
| ├── bar.py
| └── foo.py
|
├── file1.py
├── file2.py
└── file3.py
要獲取存檔文件中的文件列表,請在 ZipFile 對象上調(diào)用 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() 返回存檔文件中文件和目錄的名稱列表。要檢索有關(guān)存檔文件中文件的信息,使用 .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 對象,該對象存儲有關(guān)存檔文件的單個成員的信息。 要獲取有關(guān)存檔文件中文件的信息,請將其路徑作為參數(shù)傳遞給 .getinfo() 。 使用 getinfo() ,你可以檢索有關(guān)存檔文件成員的信息,例如上次修改文件的日期,壓縮大小及其完整文件名。 訪問 .file_size 將以字節(jié)為單位檢索文件的原始大小。
以下示例說明如何在Python REPL中檢索有關(guān)已歸檔文件的更多詳細信息。 假設(shè)已導(dǎo)入 zipfile 模塊,bar_info 與在前面的示例中創(chuàng)建的對象相同:
>>> bar_info.date_time
(2018, 10, 7, 23, 30, 10)
>>> bar_info.compress_size
2856
>>> bar_info.filename
'sub_dir/bar.py'
bar_info 包含有關(guān) bar.py 的詳細信息,例如壓縮的大小及其完整路徑。
第一行顯示了如何檢索文件的上次修改日期。 下一行顯示了如何在歸檔后獲取文件的大小。 最后一行顯示了存檔文件中 bar.py 的完整路徑。
ZipFile 支持上下文管理器協(xié)議,這就是你可以將它與with語句一起使用的原因。 操作完成后會自動關(guān)閉 ZipFile 對象。 嘗試從已關(guān)閉的 ZipFile 對象中打開或提取文件將導(dǎo)致錯誤。
提取ZIP文件
zipfile 模塊允許你通過 .extract() 和 .extractall() 從ZIP文件中提取一個或多個文件。
默認(rèn)情況下,這些方法將文件提取到當(dāng)前目錄。 它們都采用可選的路徑參數(shù),允許指定要將文件提取到的其他指定目錄。 如果該目錄不存在,則會自動創(chuàng)建該目錄。 要從壓縮文件中提取文件,請執(zhí)行以下操作:
>>> import zipfile
>>> import os
>>> os.listdir('.')
['data.zip']
>>> data_zip = zipfile.ZipFile('data.zip', 'r')
>>> # 提取單個文件到當(dāng)前目錄
>>> 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() 的調(diào)用,它顯示當(dāng)前目錄只有一個文件 data.zip 。
接下來,以讀取模式下打開 data.zip 并調(diào)用 .extract() 從中提取 file1.py 。 .extract() 返回提取文件的完整文件路徑。 由于沒有指定路徑,.extract() 會將 file1.py 提取到當(dāng)前目錄。
下一行打印一個目錄列表,顯示當(dāng)前目錄現(xiàn)在包括除原始存檔文件之外的存檔文件。 之后顯示了如何將整個存檔提取到指定目錄中。.extractall() 創(chuàng)建 extract_dir 并將 data.zip 的內(nèi)容提取到其中。 最后一行關(guān)閉ZIP存檔文件。
從加密的文檔提取數(shù)據(jù)
zipfile 支持提取受密碼保護的ZIP。 要提取受密碼保護的ZIP文件,請將密碼作為參數(shù)傳遞給 .extract() 或.extractall() 方法:
>>> import zipfile
>>> with zipfile.ZipFile('secret.zip', 'r') as pwd_zip:
... # 從加密的文檔提取數(shù)據(jù)
... pwd_zip.extractall(path='extract_dir', pwd='Quish3@o')
將以讀取模式打開 secret.zip 存檔。 密碼提供給 .extractall() ,并且壓縮文件內(nèi)容被提取到 extract_dir 。 由于with語句,在完成提取后,存檔文件會自動關(guān)閉。
創(chuàng)建新的存檔文件
要創(chuàng)建新的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語句結(jié)束后,將關(guān)閉 new_zip 。 以寫入模式打開ZIP文件會刪除壓縮文件的內(nèi)容并創(chuàng)建新存檔文件。
要將文件添加到現(xiàn)有的存檔文件,請以追加模式打開 ZipFile 對象,然后添加文件:
>>> with zipfile.ZipFile('new.zip', 'a') as new_zip:
... new_zip.write('data.txt')
... new_zip.write('latin.txt')
這里打開在上一個示例中以追加模式創(chuàng)建的 new.zip 存檔。 在追加模式下打開 ZipFile 對象允許將新文件添加到ZIP文件而不刪除其當(dāng)前內(nèi)容。 將文件添加到ZIP文件后,with語句將脫離上下文并關(guān)閉ZIP文件。
打開TAR存檔文件
TAR文件是像ZIP等未壓縮的文件存檔。 它們可以使用 gzip,bzip2 和 lzma 壓縮方法進行壓縮。 TarFile 類允許讀取和寫入TAR存檔。
下面是從存檔中讀?。?/p>
import tarfile
with tarfile.open('example.tar', 'r') as tar_file:
print(tar_file.getnames())
tarfile 對象像大多數(shù)類似文件的對象一樣打開。 它們有一個 open() 函數(shù),它采用一種模式來確定文件的打開方式。
使用“r”,“w”或“a”模式分別打開未壓縮的TAR文件以進行讀取,寫入和追加。 要打開壓縮的TAR文件,請將模式參數(shù)傳遞給 tarfile.open(),其格式為 filemode [:compression] 。 下表列出了可以打開TAR文件的可能模式:
| 模式 | 行為 |
|---|---|
| r | 以無壓縮的讀取模式打開存檔 |
| r:gz | 以gzip壓縮的讀取模式打開存檔 |
| r:bz2 | 以bzip2壓縮的讀取模式打開存檔 |
| w | 以無壓縮的寫入模式打開存檔 |
| w:gz | 以gzip壓縮的寫入模式打開存檔 |
| w:xz | 以lzma壓縮的寫入模式打開存檔 |
| a | 以無壓縮的追加模式打開存檔 |
.open() 默認(rèn)為'r'模式。 要讀取未壓縮的TAR文件并檢索其中的文件名,請使用 .getnames() :
>>> import tarfile
>>> tar = tarfile.open('example.tar', mode='r')
>>> tar.getnames()
['CONTRIBUTING.rst', 'README.md', 'app.py']
這以列表的方式返回存檔中內(nèi)容的名字。
注意:為了向你展示如何使用不同的tarfile對象方法,示例中的TAR文件在交互式REPL會話中手動打開和關(guān)閉。
通過這種方式與TAR文件交互,你可以查看運行每個命令的輸出。 通常,你可能希望使用上下文管理器來打開類似文件的對象。
此外可以使用特殊屬性訪問存檔中每個條目的元數(shù)據(jù):
>>> 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
在此示例中,循環(huán)遍歷 .getmembers() 返回的文件列表,并打印出每個文件的屬性。.getmembers() 返回的對象具有可以通過編程方式訪問的屬性,例如歸檔中每個文件的名稱,大小和上次修改時間。 在讀取或?qū)懭氪鏅n后,必須關(guān)閉它以釋放系統(tǒng)資源。
從TAR存檔中提取文件
在本節(jié)中,你將學(xué)習(xí)如何使用以下方法從TAR存檔中提取文件:
.extract().extractfile().extractall()
要從TAR存檔中提取單個文件,請使用 extract() ,傳入文件名:
>>> tar.extract('README.md')
>>> os.listdir('.')
['README.md', 'example.tar']
README.md 文件從存檔中提取到文件系統(tǒng)。 調(diào)用 os.listdir() 確認(rèn) README.md 文件已成功提取到當(dāng)前目錄中。 要從存檔中解壓縮或提取所有內(nèi)容,請使用 .extractall() :
>>> tar.extractall(path="extracted/")
.extractall() 有一個可選的 path 參數(shù)來指定解壓縮文件的去向。 這里,存檔被提取到 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
要提取文件對象以進行讀取或?qū)懭?,請使?.extractfile() ,它接收 文件名或 TarInfo 對象作為參數(shù)。 .extractfile() 返回一個可以讀取和使用的類文件對象:
>>> f = tar.extractfile('app.py')
>>> f.read()
>>> tar.close()
打開的存檔應(yīng)在讀取或?qū)懭牒笫冀K關(guān)閉。 要關(guān)閉存檔,請在存檔文件句柄上調(diào)用 .close() ,或在創(chuàng)建 tarfile對象時使用with語句,以便在完成后自動關(guān)閉存檔。 這將釋放系統(tǒng)資源,并將你對存檔所做的任何更改寫入文件系統(tǒng)。
創(chuàng)建新的TAR存檔
創(chuàng)建新的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
首先,你要創(chuàng)建要添加到存檔的文件列表,這樣你就不必手動添加每個文件。
下一行使用with光線文管理器在寫入模式下打開名為 packages.tar 的新存檔。 以寫入模式('w')打開存檔使你可以將新文件寫入存檔。 將刪除存檔中的所有現(xiàn)有文件,并創(chuàng)建新存檔。
創(chuàng)建并填充存檔后,with上下文管理器會自動關(guān)閉它并將其保存到文件系統(tǒng)。 最后三行打開剛剛創(chuàng)建的存檔,并打印出其中包含的文件的名稱。
要將新文件添加到現(xiàn)有存檔,請以追加模式('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 可以讀取和寫入使用 gzip,bzip2 和 lzma 壓縮的TAR存檔文件。 要讀取或?qū)懭雺嚎s存檔,請使用tarfile.open() ,為壓縮類型傳遞適當(dāng)?shù)哪J健?/p>
例如,要讀取或?qū)懭胧褂?gzip 壓縮的TAR存檔的數(shù)據(jù),請分別使用 '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 壓縮的存檔。 無法在追加模式下打開壓縮存檔。 要將文件添加到壓縮存檔,你必須創(chuàng)建新存檔。
一個更簡單的方式創(chuàng)建存檔
Python標(biāo)準(zhǔn)庫還支持使用 shutil 模塊中的高級方法創(chuàng)建TAR和ZIP存檔。 shutil 中的歸檔實用工具允許你創(chuàng)建,讀取和提取ZIP和TAR歸檔。 這些實用工具依賴于較底層的 tarfile 和 zipfile 模塊。
使用 shutil.make_archive() 創(chuàng)建存檔
shutil.make_archive() 至少接收兩個參數(shù):歸檔的名稱和歸檔格式。
默認(rèn)情況下,它將當(dāng)前目錄中的所有文件壓縮為 format 參數(shù)中指定的歸檔格式。 你可以傳入可選的 root_dir 參數(shù)來壓縮不同目錄中的文件。 .make_archive() 支持 zip ,tar ,bztar 和 gztar 存檔格式。
以下是使用 shutil 創(chuàng)建TAR存檔的方法:
import shutil
# shutil.make_archive(base_name, format, root_dir)
shutil.make_archive('data/backup', 'tar', 'data/')
這將復(fù)制 data / 中的所有內(nèi)容,并在文件系統(tǒng)中創(chuàng)建名為 backup.tar 的存檔并返回其名稱。 要提取存檔,請調(diào)用 .unpack_archive() :
shutil.unpack_archive('backup.tar', 'extract_dir/')
調(diào)用 .unpack_archive() 并傳入存檔名稱和目標(biāo)目錄,將 backup.tar 的內(nèi)容提取到 extract_dir/ 中。 ZIP存檔可以以相同的方式創(chuàng)建和提取。
讀取多個文件
Python支持通過 fileinput 模塊從多個輸入流或文件列表中讀取數(shù)據(jù)。 此模塊允許你快速輕松地循環(huán)遍歷一個或多個文本文件的內(nèi)容。 以下是使用 fileinput 的典型方法:
import fileinput
for line in fileinput.input()
process(line)
fileinput 默認(rèn)從傳遞給 sys.argv 的命令行參數(shù)獲取其輸入。
使用 fileinput 循環(huán)遍歷多個文件
讓我們使用 fileinput 構(gòu)建一個普通的UNIX工具 cat 的原始版本。 cat 工具按順序讀取文件,將它們寫入標(biāo)準(zhǔn)輸出。 當(dāng)在命令行參數(shù)中給出多個文件時,cat 將連接文本文件并在終端中顯示結(jié)果:
# 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()
在當(dāng)前目錄中有兩個文本文件,運行此命令會產(chǎn)生以下輸出:
$ 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 允許你檢索有關(guān)每一行的更多信息,例如它是否是第一行(.isfirstline()),行號(.lineno())和文件名(.filename())。 你可以在 這里 讀更多關(guān)于它的內(nèi)容。
總結(jié)
你現(xiàn)在知道如何使用Python對文件和文件組執(zhí)行最常見的操作。 你已經(jīng)了解使用不同的內(nèi)置模塊來讀取,查找和操作文件。
你現(xiàn)在可以用Python來實現(xiàn):
- 獲取目錄內(nèi)容和文件屬性
- 創(chuàng)建目錄和目錄樹
- 使用匹配模式匹配文件名
- 創(chuàng)建臨時文件和目錄
- 移動,重命名,復(fù)制和刪除文件或目錄
- 從不同類型的存檔文件中讀取和提取數(shù)據(jù)
- 使用 fileinput 同時讀取多個文件