@Author : Roger TX (425144880@qq.com)
@Link : https://github.com/paotong999
Python 提供有豐富的文件 I/O 支持,Python 提供了多種方式來讀取文件內(nèi)容,非常簡單、靈活。
使用 pathlib 和 os.path 來操作各種路徑,提供全局的 open() 函數(shù)來打開文件
在 Python 的 os 模塊下也包含了大量進(jìn)行文件 I/O 的函數(shù),使用這些函數(shù)來讀取、寫入文件也很方便
一、Python pathlib模塊
pathlib 模塊提供了一組面向?qū)ο蟮念?,這些類可代表各種操作系統(tǒng)上的路徑,程序可通過這些類操作路徑。

- PurePath 代表并不訪問實(shí)際文件系統(tǒng)的“純路徑”。簡單來說,PurePath 只是負(fù)責(zé)對路徑字符串執(zhí)行操作,至于該字符串是否對應(yīng)實(shí)際的路徑,它并不關(guān)心。
- Path 代表訪問實(shí)際文件系統(tǒng)的“真正路徑”。Path 對象可用于判斷對應(yīng)的文件是否存在、是否為文件、是否為目錄等。Path 同樣有兩個子類,即 PosixPath 和 WindowsPath。
PurePath 和 Path 的功能和用法
關(guān)于 PurePath 和 Path 不做過多說明,如果需要,可以查看官方文檔
PurePath 提供了不少屬性和方法,這些屬性和方法主要還是用于操作路徑字符串。
- PurePath.parts:該屬性返回路徑字符串中所包含的各部分。
- PurePath.drive:該屬性返回路徑字符串中的驅(qū)動器盤符。
- PurePath.root:該屬性返回路徑字符串中的根路徑。
- PurePath.anchor:該屬性返回路徑字符串中的盤符和根路徑。
- PurePath.parents:該屬性返回當(dāng)前路徑的全部父路徑。
- PurPath.parent:該屬性返回當(dāng)前路徑的上一級路徑,相當(dāng)于 parents[0] 的返回值。
- PurePath.name:該屬性返回當(dāng)前路徑中的文件名。
- PurePath.suffixes:該屬性返回當(dāng)前路徑中的文件所有后綴名。
- PurePath.suffix:該屬性返回當(dāng)前路徑中的文件后綴名。相當(dāng)于 suffixes 屬性返回的列表的最后一個元素。
- PurePath.stem:該屬性返回當(dāng)前路徑中的主文件名。
- PurePath.as_posix():將當(dāng)前路徑轉(zhuǎn)換成 UNIX 風(fēng)格的路徑。
- PurePath.as_uri():將當(dāng)前路徑轉(zhuǎn)換成 URI。只有絕對路徑才能轉(zhuǎn)換,否則將會引發(fā) ValueError。
- PurePath.is_absolute():判斷當(dāng)前路徑是否為絕對路徑。
- PurePath.joinpath(*other):將多個路徑連接在一起,作用類似于前面介紹的斜杠運(yùn)算符。
- PurePath.match(pattern):判斷當(dāng)前路徑是否匹配指定通配符。
- PurePath.relative_to(*other):獲取當(dāng)前路徑中去除基準(zhǔn)路徑之后的結(jié)果。
- PurePath.with_name(name):將當(dāng)前路徑中的文件名替換成新文件名。如果當(dāng)前路徑中沒有文件名,則會引發(fā) ValueError。
- PurePath.with_suffix(suffix):將當(dāng)前路徑中的文件后綴名替換成新的后綴名。如果當(dāng)前路徑中沒有后綴名,則會添加新的后綴名。
from pathlib import *
pp = PurePath('abc', 'xyz', 'wawa', 'haha') # abc\xyz\wawa\haha
# 轉(zhuǎn)成Unix風(fēng)格的路徑
pp.as_posix() # abc/xyz/wawa/haha
# 判斷當(dāng)前路徑是否匹配指定模式
print(PurePath('a/b.py').match('*.py')) # True
print(PurePath('/a/b/c.py').match('b/*.py')) # True
print(PurePath('/a/b/c.py').match('a/*.py')) # False
Path 還包含一個很常用的 iterdir() 方法,該方法可返回 Path 對應(yīng)目錄下的所有子目錄和文件
Path 還包含一個 glob() 方法,用于獲取 Path 對應(yīng)目錄及其子目錄下匹配指定模式的所有文件
from pathlib import *
# 獲取當(dāng)前目錄
p = Path('.')
# 遍歷當(dāng)前目錄下所有文件和子目錄
for x in p.iterdir():
print(x)
# 獲取上一級目錄
p = Path('../')
# 獲取上級目錄及其所有子目錄下的的py文件
for x in p.glob('**/*.py'):
print(x)
# 獲取d:/github/deeptest對應(yīng)的目錄
p = Path('d:/github/deeptest')
# 獲取上級目錄及其所有子目錄下的的py文件
for x in p.glob('**/Path_test1.py'):
print(x)
Path 讀寫文件
p = Path('a_test.txt')
# 以utf8字符集輸出文本內(nèi)容
result = p.write_text('''有一個美麗的新世界
它在遠(yuǎn)方等我
那里有天真的孩子
還有姑娘的酒窩''', encoding='utf8')
# 返回輸出的字符數(shù)
print(result)
# 指定以utf8字符集讀取文本內(nèi)容
content = p.read_text(encoding='utf8')
# 輸出讀取的文本內(nèi)容
print(content)
# 讀取字節(jié)內(nèi)容
bb = p.read_bytes()
print(bb)
二、os.path模塊常見函數(shù)
Python內(nèi)置的os模塊也可以直接調(diào)用操作系統(tǒng)提供的接口函數(shù)
import os
os.name # 操作系統(tǒng)類型
os.uname() # 詳細(xì)的系統(tǒng)信息,Windows上不可用
os.environ # 環(huán)境變量
os.environ.get('PATH') # 獲取某個環(huán)境變量的值
os.getcwd() # 獲取當(dāng)前目錄
os.chdir('../deeptest') # 改變當(dāng)前目錄
os.listdir(path) # 返回 path 對應(yīng)目錄下的所有文件和子目錄
os.mkdir(path[, mode]) # 創(chuàng)建一個目錄,mode代表權(quán)限,比如 0o777
os.makedirs(path[, mode]) # 創(chuàng)建目錄以及子目錄
os.rmdir(path) # 刪掉一個目錄
os.rename('test.txt', 'test.py') # 文件重命名
os.remove('test.py') # 刪除文件
os.path 模塊下提供了一些操作目錄的方法,這些函數(shù)可以操作系統(tǒng)的目錄本身
提供了 exists() 函數(shù)判斷該目錄是否存在
提供了 getctime() 函數(shù)來獲取該目錄的創(chuàng)建時間
提供了 getmtime() 函數(shù)來獲取該目錄的最后一次修改時間
提供了 getatime() 函數(shù)來獲取該目錄的最后一次訪問時間
提供了 getsize() 函數(shù)來獲取指定文件的大小
os.path.abspath('.') # 查看當(dāng)前目錄的絕對路徑 '/Users/michael'
os.path.join('/Users/michael', 'testdir') # 目錄拼接 '/Users/michael/testdir'
os.path.split('/Users/michael/testdir/file.txt') # 目錄拆分 ('/Users/michael/testdir', 'file.txt')
os.path.splitext('/path/to/file.txt') # 獲取后綴名 ('/path/to/file', '.txt')
os.path.commonprefix(['/usr/lib', '/usr/local/lib']) # 獲取共同前綴 /usr/l
os.path.commonpath(['/usr/lib', '/usr/local/lib']) # 獲取共同路徑 \usr
os.path.listdir('.') # 獲取當(dāng)前路徑下所有文件和目錄
os.path.isfile('os.path_test.py') # 判斷是否為文件 True
os.path.isdir('os.path_test.py') # 判斷是否為目錄 False
要列出當(dāng)前目錄下的所有目錄,只需要一行代碼:
[x for x in os.listdir('.') if os.path.isdir(x)]
['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Applications', 'Desktop', ...]
要列出所有的.py文件,只需一行代碼:
[x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']
三、fnmatch模塊:用于文件名的匹配
上面介紹的可以進(jìn)行簡單的模式匹配,但 fnmatch 模塊可以支持更復(fù)雜的匹配
fnmatch 匹配支持如下通配符:
- *:可匹配任意個任意字符。
- ?:可匹配一個任意字符。
- [字符序列]:可匹配中括號里字符序列中的任意字符。該字符序列也支持中畫線表示法。
- [!字符序列]:可匹配不在中括號里字符序列中的任意字符。
在該模塊下提供了如下函數(shù):
- fnmatch.fnmatch(filename, pattern):判斷指定文件名是否匹配指定 pattern。
from pathlib import *
import fnmatch
# 遍歷當(dāng)前目錄下所有文件和子目錄
for file in Path('.').iterdir():
# 訪問所有以_test.py結(jié)尾的文件
if fnmatch.fnmatch(file, '*_test.PY'):
print(file)
- fnmatch.fnmatchcase(filename, pattern):該函數(shù)與上一個函數(shù)的功能大致相同,只是該函數(shù)區(qū)分大小寫。
- fnmatch.filter(names, pattern):該函數(shù)對 names 列表進(jìn)行過濾,返回 names 列表中匹配 pattern 的文件名組成的子集合。
names = ['a.py', 'b.py', 'c.py', 'd.py']
# 對names列表進(jìn)行過濾
sub = fnmatch.filter(names, '[ac].py')
print(sub) # ['a.py', 'c.py']
- fnmatch.translate(pattern):該函數(shù)用于將一個 UNIX shell 風(fēng)格的 pattern 轉(zhuǎn)換為正則表達(dá)式 pattern。如下代碼示范了該函數(shù)的功能:
print(fnmatch.translate('?.py')) # (?s:.\.py)\Z
print(fnmatch.translate('[ac].py')) # (?s:[ac]\.py)\Z
print(fnmatch.translate('[a-c].py')) # (?s:[a-c]\.py)\Z
四、open函數(shù)詳解
Python 提供了一個內(nèi)置的 open() 函數(shù),傳入文件名和標(biāo)示符,用于打開指定文件。
f = open('/Users/michael/test.txt', 'r', True, encoding='utf8', errors='ignore')
- open()函數(shù)還可以傳入encoding參數(shù),設(shè)定編碼
- 遇到有些編碼不規(guī)范的文件,你可能會遇到UnicodeDecodeError,open()函數(shù)還接收一個errors參數(shù),表示如果遇到編碼錯誤后如何處理,
f = open('/Users/michael/gbk.txt', 'r', encoding='utf8', errors='ignore') - 使用 open() 函數(shù)時,其第三個參數(shù)表示是否使用緩沖,如果是 0(或 False),那么該函數(shù)打開的文件就是不帶緩沖的;如果其第三個參數(shù)是 1(或 True),則該函數(shù)打開的文件就是帶緩沖的,此時程序執(zhí)行 I/O 將具有更好的性能,也可以使用
buffering=True
如果文件不存在,open()函數(shù)就會拋出一個IOError的錯誤,并且給出錯誤碼和詳細(xì)的信息告訴你文件不存在
如果文件打開成功,接下來,調(diào)用read()方法可以一次讀取文件的全部內(nèi)容
f.read()
- 調(diào)用read()會一次性讀取文件的全部內(nèi)容
- 調(diào)用read(size)方法,每次最多讀取size個字節(jié)的內(nèi)容
- 調(diào)用readline()可以每次讀取一行內(nèi)容
- 調(diào)用readlines()一次讀取所有內(nèi)容并按行返回list
最后一步是調(diào)用close()方法關(guān)閉文件,文件使用完畢后必須關(guān)閉
f.close()
由于文件讀寫時都有可能產(chǎn)生IOError,一旦出錯,后面的f.close()就不會調(diào)用。為了保證能正確地關(guān)閉文件,我們可以使用try ... finally來實(shí)現(xiàn):
try:
f = open('/path/to/file', 'r', encoding='utf8')
print(f.read())
finally:
if f:
f.close()
但是每次都這么寫實(shí)在太繁瑣,所以,Python引入了with語句來自動幫我們調(diào)用close()方法:
with open('/path/to/file', 'r') as f:
print(f.read())
文件打開模式
| 模式 | 意義 |
|---|---|
| r | 只讀模式 |
| w | 寫模式 |
| a | 追加模式 |
| + | 讀寫模式,可與其他模式結(jié)合使用。比如 r+ 代表讀寫模式,w+ 也代表讀寫模式 |
| b | 二進(jìn)制模式,可與其他模式結(jié)合使用。b不能使用 encoding 和 errors
|

下面程序使用二進(jìn)制模式來讀取文本文件:
# 指定使用二進(jìn)制方式讀取文件內(nèi)容
f = open("read_test3.py", 'rb', True)
# 直接讀取全部文件,并調(diào)用bytes的decode將字節(jié)內(nèi)容恢復(fù)成字符串
print(f.read().decode('utf-8'))
f.close()
在調(diào)用 open() 函數(shù)時,傳入了 rb 模式,這表明采用二進(jìn)制模式讀取文件。此時文件對象的 read() 方法返回的是 bytes 對象,程序可調(diào)用 bytes 對象的 decode() 方法將它恢復(fù)成字符串。