處理檔案是我們幾乎每天都躲不開的任務之一,Python中含有幾個用于執行檔案操作的內置模塊,例如讀取檔案,移動檔案,獲取檔案屬性等,本文總結了許多值得了解的函式,這些函式可用于進行一些Python中最常見的檔案操作,可以極大地提高我們處理檔案的效率,
很多人學習python,不知道從何學起,
很多人學習python,掌握了基本語法過后,不知道在哪里尋找案例上手,
很多已經做案例的人,卻不知道如何去學習更加高深的知識,
那么針對這三類人,我給大家提供一個好的學習平臺,免費領取視頻教程,電子書籍,以及課程的源代碼!
QQ群:961562169?
打開&關閉檔案
讀取或寫入檔案前,首先要做的就是打開檔案,Python的內置函式open可以打開檔案并回傳檔案物件,檔案物件的型別取決于打開檔案的模式,可以是文本檔案物件,也可以是原始二進制檔案,或是緩沖二進制檔案物件,每個檔案物件都有諸如 read()和write()之類的方法,
你能看出以下代碼塊中存在的問題嗎?我們稍后來揭曉答案,
file = open("test_file.txt","w+")
file.read()
file.write("a new line")
Python檔案列出了所有可能的檔案模式,其中最常見的模式可見下表,但要注意一個重要規則,即:如果某一檔案存在,那么任何與w相關的模式都會截斷該檔案,并再創建一個新檔案,如果你不想覆寫原檔案,請謹慎使用此模式,或盡量使用追加模式 a,

上一個代碼塊中的問題是打開檔案后未關閉,在處理檔案后關閉檔案很重要,因為打開的檔案物件可能會出現諸如資源泄漏等不可預測的風險,以下兩種方式可以確保正確關閉檔案,
1.使用 close()
第一種方法是顯式使用close(),但較好的做法是將該代碼放在最后,因為這樣的話就可以確保在任何情況下都能關閉該檔案,而且會使代碼更加清晰,但開發人員也應負起責任,記得關閉檔案,
try:
file =open("test_file.txt","w+")
file.write("a new line")
exception Exception as e:
logging.exception(e)
finally:
file.close()
2.使用背景關系管理器,with open(...) as f
第二種方法是使用背景關系管理器,若你對此不太熟悉,還請查閱Dan Bader用Python撰寫的背景關系管理器和“ with”陳述句,用withopen() as f實作了使用__enter__ 和 __exit__ 方法來打開和關閉檔案,此外,它將try / finally陳述句封裝在背景關系管理器中,這樣我們就不會忘記關閉檔案啦,
with open("test_file","w+") as file:
file.write("a new line")
兩種方法哪個更優?這要看你使用的場景,以下示例實作了將50000條記錄寫入檔案的3種不同方式,從輸出中可見,use_context_manager_2()函式與其他函式相比性能極低,這是因為with陳述句在一個單獨函式中,基本上會為每條記錄打開和關閉檔案,這種繁瑣的I / O操作會極大地影響性能,
def _write_to_file(file, line):
with open(file, "a") as f:
f.write(line)
def _valid_records():
for i inrange(100000):
if i %2==0:
yield i
def use_context_manager_2(file):
for line in_valid_records():
_write_to_file(file, str(line))
def use_context_manager_1(file):
with open(file, "a") as f:
for line in_valid_records():
f.write(str(line))
def use_close_method(file):
f =open(file, "a")
for line in_valid_records():
f.write(str(line))
f.close()
use_close_method("test.txt")
use_context_manager_1("test.txt")
use_context_manager_2("test.txt")
# Finished use_close_method in 0.0253 secs
# Finished use_context_manager_1 in 0.0231 secs
# Finished use_context_manager_2 in 4.6302 secs
對比close()和with陳述句兩種方法
讀寫檔案
檔案打開后,開始讀取或寫入檔案,檔案物件提供了三種讀取檔案的方法,分別是 read()、readline() 和readlines(),
- 默認情況下,read(size=-1)回傳檔案的全部內容,但若檔案大于記憶體,則可選引數 size 能幫助限制回傳的字符(文本模式)或位元組(二進制模式)的大小,
- readline(size=-1) 回傳整行,最后包括字符 n,如果 size 大于0,它將從該行回傳最大字符數,
- readlines(hint=-1) 回傳串列中檔案的所有行,若回傳的字符數超過了可選引數hint,則將不回傳任何行,
在以上三種方法中,由于read() 和readlines()在默認情況下以字串或串列形式回傳完整的檔案,所以這兩種方法的記憶體效率較低,一種更有效的記憶體迭代方式是使用readline()并使其停止讀取,直到回傳空字串,空字串“”表示指標到達檔案末尾,
with open( test.txt , r ) as reader:
line = reader.readline()
while line !="":
line = reader.readline()
print(line)
以節省記憶體的方式讀取檔案
撰寫方式有兩種:write()和writelines(),顧名思義,write()能撰寫一個字串,而writelines()可撰寫一個字串串列,開發人員須在末尾添加 n,
with open("test.txt", "w+") as f:
f.write("hi")
f.writelines(["this is aline", "this is another line"])
# >>>cat test.txt
# hi
# this is a line
# this is anotherline
在檔案中寫入行
若要將文本寫入特殊的檔案型別(例如JSON或csv),則應在檔案物件頂部使用Python內置模塊json或csv,
import csv
import json
with open("cities.csv", "w+") as file:
writer = csv.DictWriter(file, fieldnames=["city", "country"])
writer.writeheader()
writer.writerow({"city": "Amsterdam", "country": "Netherlands"})
writer.writerows([
{"city": "Berlin", "country": "Germany"},
{"city": "Shanghai", "country": "China"},
])
# >>> cat cities.csv
# city,country
# Amsterdam,Netherlands
# Berlin,Germany
# Shanghai,China
with open("cities.json", "w+") as file:
json.dump({"city": "Amsterdam", "country": "Netherlands"}, file)
# >>>cat cities.json
# { "city":"Amsterdam", "country": "Netherlands" }
在檔案內移動指標
當打開檔案時,會得到一個指向特定位置的檔案處理程式,在r和w模式下,處理程式指向檔案的開頭,在a模式下,處理程式指向檔案的末尾,
tell() 和 seek()
當讀取檔案時,若沒有移動指標,那么指標將自己移動到下一個開始讀取的位置,以下2種方法可以做到這一點:tell()和seek(),
tell()以檔案開頭的位元組數/字符數的形式回傳指標的當前位置,seek(offset,whence = 0)將處理程式移至遠離wherece的offset字符處,wherece可以是:
- 0: 從檔案開頭開始
- 1:從當前位置開始
- 2:從檔案末尾開始
在文本模式下,wherece僅應為0,offset應≥0,
with open("text.txt", "w+") as f:
f.write("0123456789abcdef")
f.seek(9)
print(f.tell()) # 9 (pointermoves to 9, next read starts from 9)
print(f.read()) # 9abcdef
tell()和seek()
了解檔案狀態
作業系統中的檔案系統具有許多有關檔案的實用資訊,例如:檔案的大小,創建和修改的時間,要在Python中獲取此資訊,可以使用os或pathlib模塊,實際上,os和pathlib之間有很多共同之處,但后者更面向物件,
os
使用os.stat(“ test.txt”)可以獲取檔案完整狀態,它能回傳具有許多統計資訊的結果物件,例如st_size(檔案大小,以位元組為單位),st_atime(最新訪問的時戳),st_mtime(最新修改的時戳)等,
print(os.stat("text.txt"))
>>> os.stat_result(st_mode=33188, st_ino=8618932538,st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=16,st_atime=1597527409, st_mtime=1597527409, st_ctime=1597527409)
單獨使用os.path可獲取統計資訊,
os.path.getatime()
os.path.getctime()
os.path.getmtime()
os.path.getsize()
Pathlib
使用pathlib.Path("text.txt").stat()也可獲取檔案完整狀態,它能回傳與os.stat()相同的物件,
print(pathlib.Path("text.txt").stat())
>>>os.stat_result(st_mode=33188, st_ino=8618932538, st_dev=16777220, st_nlink=1,st_uid=501, st_gid=20, st_size=16, st_atime=1597528703, st_mtime=1597528703,st_ctime=1597528703)
下文將在諸多方面比較os和pathlib的異同,
復制,移動和洗掉檔案
Python有許多處理檔案移動的內置模塊,你在信任Google回傳的第一個答案之前,應該明白:模塊選擇不同,性能也會不同,有些模塊會阻塞執行緒,直到檔案移動完成;而其他模塊則可能異步執行,
shutil
shutil是用于移動、復制和洗掉檔案(夾)的最著名的模塊,它有3種僅供復制檔案的方法:copy(),copy2()和copyfile(),
copy() v.s. copy2():copy2()與copy()非常相似,但不同之處在于前者還能復制檔案的元資料,例如最近的訪問時間和修改時間等,不過由于Python檔案作業系統的限制,即使copy2()也無法復制所有元資料,
shutil.copy("1.csv", "copy.csv")
shutil.copy2("1.csv", "copy2.csv")
print(pathlib.Path("1.csv").stat())
print(pathlib.Path("copy.csv").stat())
print(pathlib.Path("copy2.csv").stat())
# 1.csv
# os.stat_result(st_mode=33152, st_ino=8618884732,st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=11,st_atime=1597570395, st_mtime=1597259421, st_ctime=1597570360)
# copy.csv
# os.stat_result(st_mode=33152, st_ino=8618983930,st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=11,st_atime=1597570387, st_mtime=1597570395, st_ctime=1597570395)
#copy2.csv
# os.stat_result(st_mode=33152, st_ino=8618983989, st_dev=16777220,st_nlink=1, st_uid=501, st_gid=20, st_size=11, st_atime=1597570395,st_mtime=1597259421, st_ctime=1597570395)
copy() v.s. copy2()
copy() v.s. copyfile():copy()能將新檔案的權限設定為與原檔案相同,但是copyfile()不會復制其權限模式,其次,copy()的目標可以是目錄,如果存在同名檔案,則將覆寫原檔案或創建新檔案,但是,copyfile()的目標必須是目標檔案名,
shutil.copy("1.csv", "copy.csv")
shutil.copyfile("1.csv", "copyfile.csv")
print(pathlib.Path("1.csv").stat())
print(pathlib.Path("copy.csv").stat())
print(pathlib.Path("copyfile.csv").stat())
# 1.csv
#os.stat_result(st_mode=33152, st_ino=8618884732, st_dev=16777220, st_nlink=1,st_uid=501, st_gid=20, st_size=11, st_atime=1597570395, st_mtime=1597259421,st_ctime=1597570360)
# copy.csv
#os.stat_result(st_mode=33152, st_ino=8618983930, st_dev=16777220, st_nlink=1,st_uid=501, st_gid=20, st_size=11, st_atime=1597570387, st_mtime=1597570395,st_ctime=1597570395)
# copyfile.csv
# permission(st_mode) is changed
#os.stat_result(st_mode=33188, st_ino=8618984694, st_dev=16777220, st_nlink=1,st_uid=501, st_gid=20, st_size=11, st_atime=1597570387, st_mtime=1597570395,st_ctime=1597570395)
shutil.copyfile("1.csv", "./source")
#IsADirectoryError: [Errno 21] Is a directory: ./source
copy() v.s. copyfile()
os
os 模塊內含system()函式,可在subshell中執行命令,你需要將該命令作為引數傳遞給system(),這與在作業系統上執行命令效果相同,為了移動和洗掉檔案,還可以在os模塊中使用專用功能,
# copy
os.system("cp 1.csvcopy.csv")
# rename/move
os.system("mv 1.csvmove.csv")
os.rename("1.csv", "move.csv")
# delete
os.system("rmmove.csv")
異步復制/移動檔案
到目前為止,解決方案始終是同步執行的,這意味著如果檔案過大,需要更多時間移動,那么程式可能會終止運行,如果要異步執行程式,則可以使用threading,multiprocessing或subprocess模塊,這三個模塊能使檔案操作在單獨的執行緒或行程中運行,
import threading
import subprocess
import multiprocessing
src ="1.csv"
dst ="dst_thread.csv"
thread = threading.Thread(target=shutil.copy,args=[src, dst])
thread.start()
thread.join()
dst ="dst_multiprocessing.csv"
process = multiprocessing.Process(target=shutil.copy,args=[src, dst])
process.start()
process.join()
cmd ="cp 1.csv dst_subprocess.csv"
status = subprocess.call(cmd, shell=True)
異步執行檔案操作
搜索檔案
復制和移動檔案后,你可能需要搜索與特定模式匹配的檔案名,Python提供了許多內置函式可以選擇,
Glob
glob模塊根據Unix shell使用的規則查找與指定模式匹配的所有路徑名,它支持使用通配符,
glob.glob(“ *,csv”)搜索當前目錄中所有具有csv擴展名的檔案,使用glob模塊,還可以在子目錄中搜索檔案,
>>>import glob
>>> glob.glob("*.csv")
[ 1.csv , 2.csv ]
>>> glob.glob("**/*.csv",recursive=True)
[ 1.csv , 2.csv , source/3.csv ]
os
os模塊功能十分強大,它基本上可以執行所有檔案操作,我們可以簡單地使用os.listdir()列出目錄中的所有檔案,并使用file.endswith()和file.startswith()來檢測模式,還可使用os.walk()來遍歷目錄,
import os
for file in os.listdir("."):
if file.endswith(".csv"):
print(file)
for root, dirs, files in os.walk("."):
for file in files:
if file.endswith(".csv"):
print(file)
搜索檔案名——os
pathlib
pathlib 的功能與glob模塊類似,它也可以遞回搜索檔案名,與上文基于os的解決方案相比,pathlib代碼更少,并且提供了更多面向物件的解決方案,
from pathlib importPath
p =Path(".")
for name in p.glob("**/*.csv"): # recursive
print(name)
搜索檔案名——pathlib
管理檔案路徑
管理檔案路徑是另一項常見的執行任務,它可以獲取檔案的相對路徑和絕對路徑,也可以連接多個路徑并找到父目錄等,
相對路徑和絕對路徑
os和pathlib都能獲取檔案或目錄的相對路徑和絕對路徑,
import os
import pathlib
print(os.path.abspath("1.txt")) # absolute
print(os.path.relpath("1.txt")) # relative
print(pathlib.Path("1.txt").absolute()) # absolute
print(pathlib.Path("1.txt")) # relative
檔案的相對和絕對路徑
聯接路徑
這是我們可以獨立于環境連接os和pathlib中的路徑的方式,pathlib使用斜杠創建子路徑,
import os
import pathlib
print(os.path.join("/home", "file.txt"))
print(pathlib.Path("/home") /"file.txt")
鏈接檔案路徑
獲取父目錄
dirname()是在os中獲取父目錄的函式,而在pathlib中,只需使用Path().parent函式,就能獲取父檔案夾,
import os
import pathlib
# relative path
print(os.path.dirname("source/2.csv"))
# source
print(pathlib.Path("source/2.csv").parent)
# source
# absolute path
print(pathlib.Path("source/2.csv").resolve().parent)
# /Users/<...>/project/source
print(os.path.dirname(os.path.abspath("source/2.csv")))
# /Users/<...>/project/source
獲取父檔案夾
最后簡單介紹一下os和pathlib,如Python檔案所述,pathlib是比os更面向物件的解決方案,它將每個檔案路徑表示為適當的物件,而不是字串,這對于開發人員來講非常便利:可以直接訪問物件,簡化了連接多個路徑的程序,而且能使在不同作業系統上的操作變得更加一致,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/76724.html
標籤:Python
上一篇:python 字典的一些操作
