Python/ target=_blank class=infotextkey>Python 文件處理:完整指南
Python 是一種流行的解釋型動(dòng)態(tài)類型編程語言,用于構(gòu)建 Web 服務(wù)、桌面應(yīng)用程序、自動(dòng)化腳本和機(jī)器學(xué)習(xí)項(xiàng)目。程序員在使用基于 Python 的軟件項(xiàng)目時(shí),通常必須訪問操作系統(tǒng)的文件系統(tǒng)。
例如,我們使用文本文件作為輸入,編寫文本文件作為輸出,并經(jīng)常處理二進(jìn)制文件。與任何其他流行的通用編程語言一樣,Python 也提供跨平臺(tái)文件處理功能。Python 通過幾個(gè)內(nèi)置函數(shù)和標(biāo)準(zhǔn)模塊提供文件處理功能。
在本文中,我將解釋您需要了解的有關(guān) Python 文件處理的所有信息,包括:
- 讀取文件
- 寫入文件
- 讀取文件屬性
- 創(chuàng)建新的 Python 目錄
- 讀取 Python 目錄內(nèi)容
- 刪除文件或目錄
- 執(zhí)行文件搜索
- 處理二進(jìn)制文件
- 從 Python 存檔中創(chuàng)建和提取數(shù)據(jù)
- 復(fù)制和移動(dòng)文件
- 最佳實(shí)踐
先決條件
在開始本教程之前,請確保已安裝 Python 3 解釋器。否則,請從官方版本安裝最新的 Python 解釋器。您也可以在現(xiàn)有的 Python 項(xiàng)目中使用本教程的代碼片段。
在 Python 中讀取文件
作為第一個(gè)活動(dòng),讓我們編寫一些代碼來讀取文本文件。我們需要先創(chuàng)建一個(gè)文件對象來讀取文件。
Python 提供了內(nèi)置函數(shù)來創(chuàng)建具有多種模式的文件對象,例如讀取模式、寫入模式等。創(chuàng)建一個(gè)名為的文本文件并輸入以下內(nèi)容。openmyFile.txt
Programming languages
C
C++
Python
JAVAScript
Go
現(xiàn)在,創(chuàng)建一個(gè)名為的新文件并添加以下代碼片段。mAIn.py
myFile = open("myFile.txt", "r") # or open("myFile.txt")
print(myFile.read())
myFile.close()
上面的代碼片段的第一行使用給定的文件名創(chuàng)建文件對象。內(nèi)置函數(shù)使用讀取模式創(chuàng)建文件處理程序,因?yàn)槲覀兺ㄟ^第二個(gè)參數(shù)提供了標(biāo)志。myFileopenr
請確保在使用文件后調(diào)用該方法以釋放資源。該方法返回文件內(nèi)容,因此在執(zhí)行上述代碼后,您將看到內(nèi)容,如下所示。closeread
該方法一次讀取整個(gè)文件。如果不想一次讀取所有內(nèi)容,可以使用方法的參數(shù)指定字節(jié)大小。例如,以下代碼段僅讀取前 11 個(gè)字節(jié)。readread
myFile = open("myFile.txt", "r")
print(myFile.read(11)) # Programming
myFile.close()
您將看到第一個(gè)單詞(“編程”)作為輸出 - 因?yàn)榈谝粋€(gè)單詞有 11 個(gè)字母,并且字母的大小等于 ASCII 編碼中的一個(gè)字節(jié)。如果再次打印 的結(jié)果,您將看到接下來的 11 個(gè)字節(jié)(“ languagesn”),因?yàn)槲募鈽?biāo)在上一個(gè)方法調(diào)用中移動(dòng)了 11 位。可以使用該方法將文件光標(biāo)重置回開頭,如以下示例所示。read(11)read(11)seek
myFile = open("myFile.txt")
print(myFile.read(11)) # Programming
print(myFile.read(10)) # languages
myFile.seek(0) # Sets file cursor to the beginning
print(myFile.read(11)) # Programming
myFile.close()
在大多數(shù)情況下,很容易逐行處理文件內(nèi)容。你不需要自己實(shí)現(xiàn)面向行的文件讀取機(jī)制——Python 提供了內(nèi)置的功能來逐行讀取文件。您可以使用循環(huán)和方法逐行讀取文件,如下所示。for-inreadlines
myFile = open("myFile.txt", "r")
for line in myFile.readlines():
print(line)
myFile.close()
可以使用循環(huán)獲取當(dāng)前行號(hào),因?yàn)樵摲椒▽⑹褂昧斜眍愋头祷匦小R韵麓a片段將打印行內(nèi)容及其各自的行號(hào)。for-enumeratereadlines
myFile = open("myFile.txt", "r")
for i, line in enumerate(myFile.readlines()):
print(i, line) # line number and content
myFile.close()
用 Python 編寫文件
之前,我們使用標(biāo)志創(chuàng)建了具有讀取模式的文件對象。使用讀取模式無法寫入文件,因此我們必須使用寫入模式()來寫入文件。rw
也可以使用 or 標(biāo)志同時(shí)啟用讀取和寫入模式;我們將在下面的示例中使用該標(biāo)志。r+w+w+
要開始文件編寫,讓我們通過編寫一些 Python 代碼將以下文本輸入到當(dāng)前文本。myFile.txt
Programming languages
Rust
Ruby
TypeScript
Dart
Assembly
使用以下腳本更新上述內(nèi)容。myFile.txt
myFile = open("myFile.txt", "w")
content = """Programming languages
Rust
Ruby
TypeScript
Dart
Assembly"""
myFile.write(content)
myFile.close()
在這里,我們使用 Python 多行字符串語法定義文本文件內(nèi)容,并使用該方法將內(nèi)容寫入文件。確保將寫入模式與標(biāo)志一起使用 — 否則,寫入操作將失敗并出現(xiàn)異常。
writewio.UnsupportedOperation
有時(shí),我們經(jīng)常不得不將新內(nèi)容附加到現(xiàn)有文件中。在這些情況下,由于資源消耗較高,讀取和寫入整個(gè)內(nèi)容不是一個(gè)好方法。相反,我們可以使用追加模式 ()。a
請看下面的代碼。它將向 中的列表中添加新的編程語言。myFile.txt
myFile = open("myFile.txt", "a")
myFile.write("nBash")
myFile.close()
上面的代碼片段向現(xiàn)有文件添加了新行字符 () 和一個(gè)新單詞,而無需寫入整個(gè)文件內(nèi)容。結(jié)果,我們將在編程語言列表中看到一個(gè)新條目。嘗試添加更多條目,看看會(huì)發(fā)生什么!n
在 Python 中讀取文件屬性
除了原始文件內(nèi)容外,磁盤上的文件還將包含一些元數(shù)據(jù)或文件屬性,其中包括大小、上次修改時(shí)間、上次訪問時(shí)間等。
查看下面的文件代碼,其中顯示了文件大小、上次訪問時(shí)間和上次修改時(shí)間。
import os, time
stat = os.stat("myFile.txt")
print("Size: %s bytes" % stat.st_size)
print("Last accessed: %s" % time.ctime(stat.st_atime))
print("Last modified: %s" % time.ctime(stat.st_mtime))
該函數(shù)返回一個(gè)包含許多文件屬性詳細(xì)信息的統(tǒng)計(jì)信息結(jié)果對象。在這里,我們用于獲取文件大小,獲取上次訪問的文件時(shí)間戳,并獲取上次修改的時(shí)間戳。統(tǒng)計(jì)信息結(jié)果對象可能因操作系統(tǒng)而異。例如,在 windows 操作系統(tǒng)上,您可以通過密鑰檢索特定于 Windows 的文件屬性。
os.statst_sizeat_atimest_mtimest_file_attributes
如果只需要獲取文件大小,則可以使用該方法而不檢索所有元數(shù)據(jù),如以下代碼所示。os.path.getsize
import os, time
size = os.path.getsize("myFile.txt")
print("Size: %s bytes" % size)
創(chuàng)建新的 Python 目錄
Python 提供了創(chuàng)建單個(gè)目錄的功能。以下代碼片段在當(dāng)前工作目錄中創(chuàng)建。os.mkdirmyFolder
import os
os.mkdir("myFolder")
如果您嘗試使用上述代碼遞歸創(chuàng)建多個(gè)目錄,它將失敗。例如,您無法一次創(chuàng)建,因?yàn)樗枰獎(jiǎng)?chuàng)建多個(gè)目錄。在這些情況下,該函數(shù)將為我們提供幫助,如下所示。myFolder/abcos.makedirs
import os
os.makedirs("myFolder/abc") # Creates both "myFolder" and "abc"
讀取 Python 目錄內(nèi)容
Python 還提供了一個(gè)簡單的 API 來通過函數(shù)列出目錄內(nèi)容。以下代碼片段列出了當(dāng)前工作目錄中的所有文件和目錄。os.listdir
import os
cur_dir = os.getcwd()
entries = os.listdir(cur_dir)
print("Found %s entries in %s" % (len(entries), cur_dir))
print('-' * 10)
for entry in entries:
print(entry)
執(zhí)行上述腳本后,它將顯示當(dāng)前目錄的條目,如下所示。
嘗試從其他目錄執(zhí)行腳本。然后它將顯示該特定目錄的條目,因?yàn)槲覀兪褂迷摵瘮?shù)來獲取當(dāng)前工作目錄。os.getcwd
有時(shí)我們需要遞歸列出目錄內(nèi)容。該函數(shù)幫助我們處理遞歸目錄列表。以下代碼以遞歸方式列出當(dāng)前工作目錄的所有條目。os.walk
import os
cur_dir = os.getcwd()
for root, sub_dirs, files in os.walk(cur_dir):
rel_root = os.path.relpath(root)
print("Showing entries of %s" % rel_root)
print("-" * 10)
for entry in sub_dirs + files:
print(entry)
該函數(shù)在內(nèi)部具有遞歸實(shí)現(xiàn)。它為每個(gè)條目返回三個(gè)值:os.walk
- 根
- 子
- 文件條目
在這里,我們分別使用 、 和變量,以及一個(gè) for 循環(huán)來捕獲所有條目。rootsub_dirsfiles
刪除 Python 中的文件或目錄
我們可以使用該函數(shù)刪除文件。可以之前使用該函數(shù)來防止異常。查看以下示例代碼片段。
os.removeos.path.existsos.remove
import os
file_to_remove = "myFile.txt"
if os.path.exists(file_to_remove):
os.remove(file_to_remove)
else:
print("%s doesn't exist!" % file_to_remove)
Python 標(biāo)準(zhǔn)庫還提供了刪除單個(gè)目錄的功能。它的行為類似于,如果特定目錄有一些條目,則不會(huì)刪除目錄。首先,嘗試使用以下代碼刪除單個(gè)目錄。os.rmdiros.mkdir
import os
dir_to_remove = "myFolder"
if os.path.exists(dir_to_remove):
os.rmdir(dir_to_remove)
else:
print("%s doesn't exist!" % dir_to_remove)
如果包含子文件夾或文件,上面的代碼將引發(fā)錯(cuò)誤。使用以下代碼片段以遞歸方式刪除目錄。myFolder
import os, shutil
dir_to_remove = "myFolder"
if os.path.exists(dir_to_remove):
shutil.rmtree(dir_to_remove) # Recursively remove all entries
else:
print("%s doesn't exist!" % dir_to_remove)
在 Python 中執(zhí)行文件搜索
當(dāng)我們使用自動(dòng)化腳本時(shí),有時(shí)我們需要在磁盤上執(zhí)行文件搜索。例如,程序員經(jīng)常需要通過他們的 Python 腳本查找日志文件、圖像文件和各種文本文件。在 Python 中執(zhí)行文件搜索有幾種不同的方法:
- 使用該函數(shù)查找所有條目,并使用循環(huán)中的條件檢查每個(gè)條目os.listdiriffor
- 使用該函數(shù)遞歸查找所有條目,并使用循環(huán)中的條件驗(yàn)證每個(gè)條目。os.walktreeiffor
- 使用該函數(shù)查詢所有條目并僅獲取所需的條目glob.glob
總體而言,第三種方法最適合大多數(shù)方案,因?yàn)樗哂袃?nèi)置的過濾支持,非常好的性能,并且需要開發(fā)人員端的最少代碼(更多Pythonic)。讓我們使用 Python glob 模塊實(shí)現(xiàn)文件搜索。
import glob, os
query = "**/*.py"
entries = glob.glob(query, recursive=True)
no_of_entries = len(entries)
if no_of_entries == 0:
print("No results for query: %s" % query)
else:
print("Found %s result(s) for query: %s" % (no_of_entries, query))
print("-" * 10)
for entry in entries:
print(entry)
上面的代碼以遞歸方式列出了當(dāng)前目錄中的所有 Python 源文件。查詢變量中的前兩個(gè)星號(hào) () 指示 Python 搜索每個(gè)子目錄,而最后一個(gè)星號(hào)表示任何文件名。**
運(yùn)行上述腳本。您將看到 Python 源文件,如下所示。
嘗試通過更改變量來搜索不同的文件類型。query
在 Python 中處理二進(jìn)制文件
早些時(shí)候,我們處理了文本文件。默認(rèn)情況下,內(nèi)置函數(shù)使用文本模式 () 創(chuàng)建文件對象。非文本文件(如圖像文件、zip 文件和視頻文件)不能被視為純文本文件——因?yàn)闆]有可讀的英語句子二進(jìn)制文件。因此,我們必須通過字節(jié)級(jí)(或位級(jí))處理將二進(jìn)制文件視為非文本文件。opent
要開始使用二進(jìn)制文件處理,讓我們編寫一個(gè)包含一些字節(jié)的二進(jìn)制文件。我們將以下字節(jié)保存到 .myFile.bin
01010000 01111001 01110100 01101000 01101111 01101110
為簡單起見,我們可以分別使用以下十進(jìn)制值表示上述字節(jié)。
80 121 116 104 111 110
現(xiàn)在,將以下代碼添加到 Python 源文件并執(zhí)行它以創(chuàng)建二進(jìn)制文件。
myBinaryFile = open("myFile.bin", "wb") # wb -> write binary
bytes = bytearray([80, 121, 116, 104, 111, 110])
myBinaryFile.write(bytes)
myBinaryFile.close()
在這里,我們將一個(gè)字節(jié)數(shù)組實(shí)例傳遞給文件對象的方法。另外,請注意,我們使用二進(jìn)制模式 () 來創(chuàng)建文件對象。執(zhí)行上述代碼片段后,打開使用您喜歡的文本編輯器新創(chuàng)建的代碼。您將看到以下結(jié)果。writebmyFile.bin
我們收到了“Python”作為輸出,因?yàn)樽止?jié)數(shù)組的字節(jié)表示已知的 ASCII 字符。例如,() 表示 ASCII 編碼中的字母。即使我們將可讀文本保存在二進(jìn)制文件中,幾乎所有二進(jìn)制文件都包含不可讀的字節(jié)流。嘗試通過文本編輯器打開圖像文件。8001010000P
現(xiàn)在我們可以在以下示例代碼中看到二進(jìn)制文件讀取操作。
myBinaryFile = open("myFile.bin", "rb")
bytes = myBinaryFile.read()
print(bytes) # bytearray(b'Python')
print("Bytes: ", list(bytes)) # Bytes: [80, 121, 116, 104, 111, 110]
myBinaryFile.close()
Python 使用二進(jìn)制模式的方法返回字節(jié)。在這里,我們使用構(gòu)造函數(shù)將字節(jié)轉(zhuǎn)換為實(shí)例。readbytearraybytearray
從 Python 存檔創(chuàng)建和提取
程序員經(jīng)常將歸檔文件與基于 Python 的 Web 應(yīng)用程序、Web 服務(wù)、桌面應(yīng)用程序和實(shí)用程序一起使用,以一次輸出或輸入多個(gè)文件。例如,如果您正在構(gòu)建基于 Web 的文件管理器,則可以為用戶提供一項(xiàng)功能,通過以編程方式生成的 zip 文件一次下載多個(gè)文件。
Python 標(biāo)準(zhǔn)庫通過該模塊提供存檔文件處理 API。首先,讓我們創(chuàng)建一個(gè)包含 內(nèi)容的存檔。請看下面的代碼。確保在運(yùn)行代碼片段之前創(chuàng)建并添加一些文件。shutilmyFoldermyFolder
import shutil
output_file = "myArchive"
input_dir = "myFolder"
shutil.make_archive(output_file, "zip", input_dir)
您可以使用以下代碼將存檔文件提取到其中。myNewFolder
import shutil
input_file = "myArchive.zip"
output_dir = "myNewFolder"
shutil.unpack_archive(input_file, output_dir)
復(fù)制和移動(dòng)文件
該模塊還提供跨平臺(tái) API 函數(shù)來復(fù)制和移動(dòng)文件。請看以下示例。shutil
import shutil
# copy main.py -> main_copy.py
shutil.copy("main.py", "main_copy.py")
# move (rename) main_copy.py -> main_backup.py
shutil.move("main_copy.py", "main_backup.py")
# recursive copy myFolder -> myFolder_copy
shutil.copytree("myFolder", "myFolder_copy")
# move (rename) myFolder_copy -> myFolder_backup
# if myFolder_backup exists, source is moved inside folder
shutil.move("myFolder_copy", "myFolder_backup")
print("Done.")
Python 文件處理最佳實(shí)踐
程序員遵循各種編碼實(shí)踐。同樣,Python 程序員在處理文件時(shí)也遵循不同的編碼實(shí)踐。
例如,一些程序員手動(dòng)使用 try-finally 阻止和關(guān)閉文件處理程序。一些程序員通過省略方法調(diào)用來讓垃圾回收器關(guān)閉文件處理程序——這不是一個(gè)好的做法。同時(shí),其他程序員使用該語法來處理文件處理程序。closewith
在本節(jié)中,我將總結(jié)一些在 Python 中處理文件的最佳實(shí)踐。首先,查看遵循文件處理最佳做法的以下代碼。
def print_file_content(filename):
with open(filename) as myFile:
content = myFile.read()
print(content)
file_to_read = "myFile.txt"
try:
print_file_content(file_to_read)
except:
print("Unable to open file %s " % file_to_read)
else:
print("Successfully print %s's content" % file_to_read)
在這里,我們使用關(guān)鍵字隱式關(guān)閉文件處理程序。此外,我們使用 try-except 塊處理可能的異常。在使用 Python 文件處理時(shí),可以確保代碼具有以下幾點(diǎn)。with
- 永遠(yuǎn)不要忽略異常 - 特別是對于長時(shí)間運(yùn)行的 Python 進(jìn)程。但是,可以忽略簡單實(shí)用程序腳本的異常,因?yàn)槲唇?jīng)處理的異常會(huì)阻止實(shí)用程序腳本繼續(xù)
- 如果不使用該語法,請確保正確關(guān)閉打開的文件處理程序。Python 垃圾收集器將清理未關(guān)閉的文件處理程序,但通過我們的代碼關(guān)閉文件處理程序以避免不必要的資源使用總是好的with
- 確保在代碼庫中統(tǒng)一文件處理語法。例如,如果使用關(guān)鍵字來處理文件,請確保對處理文件的所有位置使用相同的語法with
- 避免在使用多個(gè)處理程序讀取或?qū)懭霑r(shí)再次重新打開同一文件。請改用 and 方法,如下所示:flushseek
def process_file(filename):
with open(filename, "w+") as myFile:
# w+: read/write and create if doesn't exist unlike r+
# Write content
myFile.write("Hello Python!")
print("Cursor position: ", myFile.tell()) # 13
# Reset internal buffer
myFile.flush()
# Set cursor to the beginning
myFile.seek(0)
print("Cursor position: ", myFile.tell()) # 0
# Print new content
content = myFile.read()
print(content)
print("Cursor position: ", myFile.tell()) # 13
file_to_read = "myFile.txt"
try:
process_file(file_to_read)
except:
print("Unable to process file %s " % file_to_read)
else:
print("Successfully processed %s" % file_to_read)
以上內(nèi)容首先將字符串保存到文件中。之后,它通過重置內(nèi)部緩沖區(qū)再次讀取新添加的內(nèi)容。該方法清除內(nèi)存中臨時(shí)保存的數(shù)據(jù),因此下一次讀取將返回新添加的內(nèi)容。此外,我們需要使用方法調(diào)用將光標(biāo)重置為開頭,因?yàn)樵摲椒▽⑵湓O(shè)置為結(jié)尾。flushseek(0)write
結(jié)論
Python為程序員提供了一個(gè)簡單的語法。因此,幾乎所有的文件操作都易于實(shí)現(xiàn)。但是,Python 在標(biāo)準(zhǔn)庫設(shè)計(jì)方面存在一些問題,因此同一事物有多個(gè) API 函數(shù)。因此,您必須根據(jù)您的要求選擇最合適的標(biāo)準(zhǔn)模塊。
此外,與其他流行的編程語言相比,Python 是一種速度較慢的語言。考慮到這一點(diǎn),請確保在不使用太多資源的情況下優(yōu)化 Python 腳本。例如,您可以通過逐行處理大型文本文件來優(yōu)化性能,而無需一次處理整個(gè)內(nèi)容。
在本教程中,我們討論了通用文本文件處理和二進(jìn)制文件處理。如果您需要處理特定的文件格式,選擇更好的庫或標(biāo)準(zhǔn)模塊可能是值得的。例如,您可以使用 csv 標(biāo)準(zhǔn)模塊處理 CSV 文件,使用 PyPDF2 庫處理 PDF 文件。此外,pickle 標(biāo)準(zhǔn)模塊可幫助您使用文件存儲(chǔ)(和加載)Python 數(shù)據(jù)對象。