URLhttps://learnscript.net/zh-hant/python/file-system/files/file-objects/
    複製連結移至說明  範例

    如何使用 Python 檔案物件讀取及寫入檔案?以及檔案指標,關閉檔案,取得檔案物件資訊等問題

    閱讀 28:18·字數 8491·更新 
    Youtube 頻道
    訂閱 375

    本節主要講述如何使用 Python 檔案物件讀取及寫入檔案,至於如何開啟檔案,以及編碼,換行,緩沖等問題,請參考如何使用 open 函式開啟檔案?以及編碼,解碼,換行,緩沖等問題一節。

    Python 檔案物件

    Python 檔案物件負責檔案的讀取,寫入,關閉等操作,通常作為內建函式open的傳回值,根據所傳遞的參數的不同,open函式傳回的 Python 檔案物件可能是以下型別之一,TextIOWrapper(文字檔案),BufferedReader(擁有緩沖區的唯讀二進位檔案),BufferedWriter(擁有緩沖區的唯寫二進位檔案),BufferedRandom(擁有緩沖區的可讀取及寫入的二進位檔案),FileIO(停用緩沖區的二進位檔案)。

    讀取檔案中的字元或位元組

    當檔案物件允許讀取時,可以使用read方法讀取本文檔案中的字元或二進位檔案中的位元組,在讀取的同時,檔案指標將被移動。如果是讀取字元,那麽read方法傳回str字串,如果是讀取位元組,那麽read方法傳回bytes位元組序列(你可以將其視為另類的「字串」)。

    read(size=-1)

    size 參數

    size參數表示能夠讀取的最大字元數(文字檔案)或最大位元組數(二進位檔案),如果該參數為None或小於0,那麽將讀取從目前指標位置到檔案末尾的所有字元或位元組。

    這裏需要指出,英文字母和漢字均被視為一個字元。

    對於停用緩沖區的二進位檔案,檔案物件的readall方法可以讀取從目前指標位置到檔案末尾的所有字元或位元組,該函式的執行效果與呼叫read方法但不給出size參數的效果類似。

    readall()

    開啟檔案

    關於如何在 Pythonopen函式中設定緩沖策略,請檢視指定緩沖策略一段。

    在下面的範例中,通過read方法讀取的前 4 個位元組,包括「你」字的 3 個位元組,以及「好」字的第一個位元組。

    read.py
    # 請將命令列跳躍至腳本檔案 read.py 所在的目錄,然後執行他
    # 讀取檔案中的字元
    with open('read.txt', 'r', encoding='utf8') as file:
    	print(f'讀取 4 個字元:{file.read(4)}')
    	print(f'讀取剩余字元:{file.read()}')
    
    # 讀取檔案中的位元組 with open('read.txt', 'rb') as file: print(f'讀取 4 個位元組:{file.read(4)}') print(f'讀取剩余位元組:{file.read()}')
    # 讀取檔案中的位元組,但停用緩沖區 with open('read.txt', 'rb', buffering=0) as file: print(f'讀取 4 個位元組:{file.read(4)}') print(f'讀取剩余位元組:{file.readall()}')
    read.txt
    你好,world!
    Yes,很好
    讀取 4 個字元:你好,w
    讀取剩余字元:orld!
    Yes,很好
    讀取 4 個位元組:b'\xe4\xbd\xa0\xe5'
    讀取剩余位元組:b'\xa5\xbd\xef\xbc\x8cworld\xef\xbc\x81\r\nYes\xef\xbc\x8c\xe5\xbe\x88\xe5\xa5\xbd'
    讀取 4 個位元組:b'\xe4\xbd\xa0\xe5'
    讀取剩余位元組:b'\xa5\xbd\xef\xbc\x8cworld\xef\xbc\x81\r\nYes\xef\xbc\x8c\xe5\xbe\x88\xe5\xa5\xbd'

    當指標位於檔案末尾時,read 方法將傳回空字串或空位元組序列

    當檔案指標位於檔案末尾時,呼叫檔案物件的read方法將傳回空字串''(文字檔案)或空位元組序列b''(二進位檔案),而不是空值None

    read_eof.py
    # 請將命令列跳躍至腳本檔案 read_eof.py 所在的目錄,然後執行他
    with open('read.txt', 'r', encoding='utf8') as file:
    	file.read()
    	print(f'從檔案末尾讀取字元:{file.read()}')
    
    with open('read.txt', 'rb') as file: file.read() print(f'從檔案末尾讀取位元組:{file.read()}')
    從檔案末尾讀取字元:
    從檔案末尾讀取位元組:b''

    使用 Python 檔案物件讀取檔案中的行

    當檔案物件允許讀取時,可使用readline方法讀取從目前指標位置到行末尾或檔案末尾的所有字元或位元組(包括換行字元和歸位字元,如果是文字檔案,那麽由open函式的newline參數決定),並可以指定能夠讀取的最大字元數或最大位元組數。

    檔案物件的readline方法的傳回值是字串或bytes位元組序列,在讀取字元的同時,檔案指標將被移動。

    readline(size=-1)

    size 參數

    size參數為能夠讀取的最大字元數(文字檔案)或最大位元組數(二進位檔案),預設為-1,表示沒有最大字元數或最大位元組數的限製。

    開啟檔案

    關於open函式的newline參數,你可以檢視指定換行₁的處理方式一段。

    在下面的範例中,由於我們分別使用read方法讀取了 4 個字元和 4 個位元組,因此,接下來的readline方法會從第 5 個字元和第 5 個位元組開始讀取。

    readline.py
    # 請將命令列跳躍至腳本檔案 readline.py 所在的目錄,然後執行他
    # 讀取文字檔案中的行
    with open('read.txt', 'r', encoding='utf8') as file:
    	print(f'讀取 4 個字元:{file.read(4)}')
    	# 包括 \n
    	print(f'讀取行:{file.readline()}')
    	print(f'讀取行(最多 2 個字元):{file.readline(2)}')
    	print(f'讀取行:{file.readline()}')
    
    # 讀取二進位檔案中的行 with open('read.txt', 'rb') as file: print(f'讀取 4 個位元組:{file.read(4)}') # 包括 \r\n print(f'讀取行:{file.readline()}') print(f'讀取行(最多 2 個位元組):{file.readline(2)}') print(f'讀取行:{file.readline()}')
    讀取 4 個字元:你好,w
    讀取行:orld!

    讀取行(最多 2 個字元):Ye
    讀取行:s,很好
    讀取 4 個位元組:b'\xe4\xbd\xa0\xe5'
    讀取行:b'\xa5\xbd\xef\xbc\x8cworld\xef\xbc\x81\r\n'
    讀取行(最多 2 個位元組):b'Ye'
    讀取行:b's\xef\xbc\x8c\xe5\xbe\x88\xe5\xa5\xbd'

    當指標位於檔案末尾時,readline 方法將傳回空字串或空位元組序列

    當檔案指標位於檔案末尾時,呼叫檔案物件的readline方法將傳回空字串''(文字檔案)或空位元組序列b''(二進位檔案),而不是空值None

    readline_eof.py
    # 請將命令列跳躍至腳本檔案 readline_eof.py 所在的目錄,然後執行他
    with open('read.txt', 'r', encoding='utf8') as file:
    	file.read()
    	print(f'從檔案末尾讀取一行:{file.readline()}')
    
    with open('read.txt', 'rb') as file: file.read() print(f'從檔案末尾讀取一行:{file.readline()}')
    從檔案末尾讀取一行:
    從檔案末尾讀取一行:b''

    當檔案物件允許讀取時,可使用readlines方法讀取從目前指標位置到檔案末尾的所有行(包括換行字元和歸位字元,如果是文字檔案,由open函式的newline參數決定),並可以指定能夠讀取的最大字元數或最大位元組數,在讀取的同時,檔案指標將被移動。

    檔案物件的readlines方法的傳回值是一個串列物件,串列中的元素對應從檔案讀取到的行(一個字串或位元組序列)。

    readlines(hint=-1)

    hint 參數

    hint參數為能夠讀取的最大字元數(文字檔案)或最大位元組數(二進位檔案),預設為-1,表示沒有最大字元數或最大位元組數的限製。

    需要說明的是,即便已經超出了最大字元數或最大位元組數,檔案物件的readlines方法依然會讀取至行的末尾或檔案末尾。

    在下面的範例中,我們分別使用readlines方法讀取了 12 個字元和 12 個位元組,檔案指標將到達第二行和留在第一行,對於這兩種情況,檔案物件均會讀取至行末尾。

    readlines.py
    # 請將命令列跳躍至腳本檔案 readlines.py 所在的目錄,然後執行他
    # 讀取文字檔案中的多個行
    with open('readlines.txt', 'r', encoding='utf8') as file:
    	print(f'讀取多個行(最多 12 個字元):{file.readlines(12)}')
    	print(f'讀取剩余的行:{file.readlines()}')
    
    # 讀取二進位檔案中的多個行 with open('readlines.txt', 'rb') as file: print(f'讀取多個行(最多 12 個位元組):{file.readlines(12)}') print(f'讀取剩余的行:{file.readlines()}')
    readlines.txt
    你好,world!
    Yes,很好
    What a day!
    讀取多個行(最多 12 個字元):['你好,world!\n', 'Yes,很好\n']
    讀取剩余的行:['What a day!']
    讀取多個行(最多 12 個位元組):[b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8cworld\xef\xbc\x81\r\n']
    讀取剩余的行:[b'Yes\xef\xbc\x8c\xe5\xbe\x88\xe5\xa5\xbd\r\n', b'What a day!']

    當指標位於檔案末尾時,readlines 方法將傳回空的串列

    當檔案指標位於檔案末尾時,呼叫檔案物件的readlines方法將傳回空的串列[],而不是空值None

    readlines_eof.py
    # 請將命令列跳躍至腳本檔案 readlines_eof.py 所在的目錄,然後執行他
    with open('readlines.txt', 'r', encoding='utf8') as file:
    	file.read()
    	print(f'從檔案末尾讀取多個行:{file.readlines()}')
    
    with open('readlines.txt', 'rb') as file: file.read() print(f'從檔案末尾讀取多個行:{file.readlines()}')
    從檔案末尾讀取多個行:[]
    從檔案末尾讀取多個行:[]

    通過周遊 Python 檔案物件讀取檔案中的行

    除了使用方法readlinereadlines來讀取檔案中的行,你還可以通過周遊檔案物件的方式來讀取行,因為檔案物件也是疊代器物件。

    for.py
    # 請將命令列跳躍至腳本檔案 for.py 所在的目錄,然後執行他
    # 使用 for 陳述式周遊檔案
    with open('readlines.txt', 'r', encoding='utf8') as file:
    	for l in file:
    		print(l)
    你好,world!

    Yes,很好

    What a day!

    將二進位檔案中的位元組讀取至類位元組序列物件

    對於二進位檔案,你可以使用檔案物件的readinto方法,將檔案中的位元組讀取至類位元組序列(bytes-like)物件。readinto方法會嘗試讀取足夠多的位元組,以填入類位元組序列物件,並將實際讀取的位元組數作為傳回值(int型別)。

    readinto(b)

    b 參數

    b參數是一個可以寫入的類位元組序列物件。

    在下面的範例中,我們嘗試將二進位檔案中剩余的 23 個位元組讀取至bytearray,因此bytearray的最後 7 個位元組為\x00

    readinto.py
    # 請將命令列跳躍至腳本檔案 readinto.py 所在的目錄,然後執行他
    # 讀取位元組至 bytearray
    with open('read.txt', 'rb') as file:
    	ba8 = bytearray(8)
    	# 嘗試讀取 8 個位元組以填入 ba
    	print(f'讀取了 {file.readinto(ba8)} 個位元組')
    	print(ba8)
    
    ba30 = bytearray(30) # 讀取剩余的 23 個位元組 print(f'讀取了 {file.readinto(ba30)} 個位元組') print(ba30)
    讀取了 8 個位元組
    bytearray(b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc')
    讀取了 23 個位元組
    bytearray(b'\x8cworld\xef\xbc\x81\r\nYes\xef\xbc\x8c\xe5\xbe\x88\xe5\xa5\xbd\x00\x00\x00\x00\x00\x00\x00')

    將字元或位元組寫入檔案

    當檔案物件允許寫入時,可使用write方法將字元或位元組寫入本文檔案或二進位檔案,並傳回寫入的字元數或位元組數。

    write(s|b)

    s,b 參數

    s參數為需要寫入的字串,b參數為包含需要寫入的位元組的類位元組序列物件。

    utf8編碼中的大部分漢字占用 3 個位元組,字串'第一行\n第二行'在解碼後是一個包含 19 個位元組的bytes物件。

    write.py
    # 請將命令列跳躍至腳本檔案 write.py 所在的目錄,然後執行他
    # 將字元寫入檔案
    with open('write.txt', 'w', encoding='utf8') as file:
    	c = file.write('第一行\n第二行')
    	print(f'寫入 {c} 個字元')
    
    # 將位元組寫入檔案 with open('writeb.txt', 'wb') as file: bs = '第一行\n第二行'.encode() c = file.write(bs) print(f'寫入 {c} 個位元組')
    寫入 7 個字元
    寫入 19 個位元組

    將行寫入檔案

    當檔案物件允許寫入時,可以使用writelines方法將多行資料(字元或位元組)寫入本文檔案或二進位檔案。

    writelines(lines)

    lines 參數

    lines參數是一個疊代器物件,疊代器中的元素是需要寫入的字串,或包含需要寫入的位元組的類位元組序列(bytes-like)物件。

    writelines 方法不會自動為每一行新增換行字元或歸位字元

    檔案物件的writelines方法並不會自動為每一行新增換行字元或歸位字元(或者換行字元或歸位字元對應的位元組),因此需要開發人員自行處理。

    在下面的範例中,我們自行為第一行新增換行字元和其對應的位元組。

    writelines.py
    # 請將命令列跳躍至腳本檔案 writelines.py 所在的目錄,然後執行他
    # 將多個行寫入檔案
    with open('writelines.txt', 'w', encoding='utf8') as file:
    	file.writelines(('第一行\n', '第二行'))
    
    # 將多個行寫入檔案 with open('writelinesb.txt', 'wb') as file: file.writelines(('第一行\n'.encode(), '第二行'.encode()))

    將緩沖區中的內容寫入檔案

    對於支援寫入操作並使用了緩沖區的檔案物件,其方法flush可用於立即將緩沖區中的內容寫入檔案,當然,很多情況下,呼叫該方法是沒有必要的。

    對於不支援寫入操作或停用了緩沖區的檔案物件,呼叫flush方法不會產生任何效果。

    flush()

    在下面的範例中,由於緩沖區的大小為4b'abc'會保留在緩沖區中,因為他們僅占用 3 個位元組。

    flush.py
    # 請將命令列跳躍至腳本檔案 flush.py 所在的目錄,然後執行他
    with open('flush.txt', 'wb', buffering=4) as file:
    	file.write(b'abc')
    	# 將緩沖區中的 abc 寫入檔案
    	file.flush()

    取得和設定檔案指標的位置

    在絕大多數情況下,檔案指標的位置是檔案物件從檔案讀取內容或將內容寫入檔案的開始位置,並且會在讀取或寫入內容後發生變化,以方便下一次的讀取和寫入。在開啟檔案之後,檔案指標的位置可能在檔案開頭或末尾。

    無論是文字檔案還是二進位檔案,檔案指標的位置總是以位元組為單位,因此,對於文字檔案對應的檔案物件,需要得知字元對應的位元組數才能正確的移動檔案指標,否則可能導致錯誤的發生。

    檔案物件提供了名稱為tell的方法,用於獲得目前檔案指標的位置,該方法傳回一個整數,如果傳回值為0,則表示指標位於檔案的開頭。

    tell()

    檔案物件提供了名稱為seek的方法,用於改變檔案指標的位置。

    seek(offset, whence)

    offset 參數

    offset參數是一個表示檔案指標位移值的整數,當offset大於0時,檔案指標將向檔案末尾移動指定的位元組數,當offset小於0時,檔案指標將向檔案開頭移動指定的位元組數。

    whence 參數

    whence參數是一個整數,如果為0(預設值,os.SEEK_SET),則從檔案的開頭位置計算offset表示的指標位移值,如果為1os.SEEK_CUR),則從目前檔案指標位置計算offset表示的指標位移值,如果為2os.SEEK_END),則從檔案的末尾位置計算offset表示的指標位移值。

    在下面的範例中,我們開啟了文字檔案read.txt,因此要移動檔案指標的位置,需要知道字元所占用的位元組數,這裏檔案開頭處的「你好」兩字占用了 6 個位元組。

    pointer.py
    # 請將命令列跳躍至腳本檔案 pointer.py 所在的目錄,然後執行他
    file = open('read.txt', 'r', encoding='utf8')
    
    # 開始時指標位於檔案開頭 print(f'指標位置:{file.tell()}') # 將指標移動到「你好」之後,「你好」占用了 6 個位元組 file.seek(6, 0) # 讀取下一個字元「,」 print(file.read(1)) # 由於「,」占用 3 個位元組,因此指標位置現在為 9 print(f'指標位置:{file.tell()}')
    指標位置:0

    指標位置:9

    檔案物件不能將檔案指標的位置設定為負數

    無論如何,檔案物件無法將檔案指標的位置設定為負數,否則將導致例外狀況。不過,使檔案指標的位置超出檔案末尾並沒有問題。

    在下面的範例中,我們將檔案指標移動到檔案末尾後 100 的位置,但之後的read方法並沒有擲回例外狀況。

    pointer_err.py
    # 請將命令列跳躍至腳本檔案 pointer_err.py 所在的目錄,然後執行他
    file = open('read.txt', 'r+b')
    
    # 檔案指標位置將超出檔案末尾 file.seek(100, 2) print(f'指標位置:{file.tell()}') # 讀取檔案不會產生例外狀況 print(file.read())
    # ERROR 檔案指標的位置不能是負數 file.seek(-100, 0)
    指標位置:131
    b''
    OSError: [Errno ] Invalid argument

    判斷 Python 檔案物件是否支援隨機存取

    當檔案物件不支援檔案的隨機存取時,你將不能取得或設定檔案指標的位置,即不能呼叫檔案物件的tellseek方法,否則會引發例外狀況OSError

    通過檔案物件的seekable方法,可以判斷檔案物件是否支援隨機存取,如果該方法傳回True,那麽表示支援隨機存取。

    seekable()

    判斷 Python 檔案物件是否可以讀取或寫入

    檔案物件的readablewritable方法傳回一個布林值,用於表示檔案物件是否支援讀取和寫入操作(主要由open函式的mode參數決定),傳回True表示支援。

    readable()
    writable()

    開啟檔案

    關於open函式的mode參數,你可以檢視指定檔案開啟模式一段。

    info.py
    # 請將命令列跳躍至腳本檔案 info.py 所在的目錄,然後執行他
    with open('read.txt', 'r', encoding='utf8') as file:
    	print(f'讀取:{file.readable()},寫入:{file.writable()}')
    
    with open('read.txt', 'r+b') as file: print(f'讀取:{file.readable()},寫入:{file.writable()}')
    讀取:True,寫入:False
    讀取:True,寫入:True

    關閉檔案

    檔案物件擁有一個名稱為close的方法,用於關閉檔案物件對應的檔案,一旦該方法被呼叫,你將無法通過檔案物件對檔案實施操作,比如,讀取和寫入。

    close()

    確保在程式碼中呼叫檔案物件的 close 方法

    雖然檔案物件會在終結時嘗試關閉檔案,但在程式碼中明確的呼叫檔案物件的close方法,依然是保險的做法,否則通過檔案物件寫入的內容存在無法儲存至檔案的可能。

    如果使用with陳述式和open函式開啟檔案,那麽不需要呼叫檔案物件的close方法,因為,該方法會被檔案物件的__exit__方法直接或間接呼叫。

    with 陳述式

    關於with陳述式,你可以檢視Python with 陳述式,with 陳述式上下文管理器介紹一節。

    在下面的範例中,由於檔案物件已經呼叫了close方法,因此,再次通過檔案物件寫入資料將導致例外狀況。

    close.py
    # 請將命令列跳躍至腳本檔案 close.py 所在的目錄,然後執行他
    file = open('close.txt', 'w', encoding='utf8')
    file.write('寫入一些資料!')
    # 關閉檔案
    file.close()
    
    # ERROR 檔案已經關閉 file.write('再寫入一些資料?')
    ValueError: I/O operation on closed file.

    判斷 Python 檔案物件對應的檔案是否已經關閉

    通過檔案物件的closed屬性,可以檢查檔案物件對應的檔案是否已經關閉,如果closed屬性為True,那麽表明檔案已經關閉。

    closed

    在下面的範例中,我們通過with陳述式開啟檔案,在with陳述式結束時,檔案物件的__exit__方法將被呼叫,而__exit__方法將呼叫close方法關閉檔案。

    closed.py
    # 請將命令列跳躍至腳本檔案 closed.py 所在的目錄,然後執行他
    # with 陳述式結束之後,檔案將被關閉
    with open('close.txt', 'w', encoding='utf8') as file:
    	file.write('寫入一些資料!')
    
    # 檢查檔案是否被關閉 if file.closed: print('檔案 close.txt 已經關閉')
    檔案 close.txt 已經關閉

    截斷檔案

    對於支援寫入的檔案物件,可以使用truncate方法將檔案截斷,並傳回截斷後的大小,該方法不會改變檔案指標的位置。

    truncate(size=None)

    size 參數

    size參數是一個整數(大於等於0),表示在第幾個位元組處截斷檔案。如果該參數被忽略或者為None,則在目前檔案指標的位置截斷檔案(註意,實際效果可能並非如此)。

    事實上,size參數所指示的位置可以超出檔案末尾,這會導致檔案的末尾被填入數個位元組\x00

    在下面的範例中,「真是美好」占用了 12 個位元組,呼叫方法truncate之後檔案指標沒有發生變化。

    truncate.py
    # 請將命令列跳躍至腳本檔案 truncate.py 所在的目錄,然後執行他
    with open('truncate.txt', 'r+', encoding="utf8") as file:
    	# 顯示檔案內容
    	print(file.read())
    	# 保留「真是美好」
    	print(f'從第 12 個位元組處截斷:{file.truncate(12)}')
    	# 檔案指標位置不會被 truncate 方法改變
    	print(f'目前指標位置:{file.tell()}')
    truncate.txt
    真是美好的一天
    真是美好的一天
    從第 12 個位元組處截斷:12
    目前指標位置:21

    取得 Python 檔案物件的其他資訊

    在使用內建函式open建立檔案物件時,我們會使用一些參數來設定檔案物件的某些功能,而檔案物件則提供了以下變數,屬性,方法來說明他們。

    檔案物件的name屬性是檔案路徑或檔案描述器(file descriptor)對應的整數,這與傳遞給open函式的file參數有關。

    檔案物件的fileno方法傳回檔案描述器對應的整數,是的,即便你向open函式傳遞檔案路徑,但最終使用的是檔案描述器。

    檔案物件的mode屬性表示開啟模式,這與傳遞給open函式的mode參數有關。

    name
    fileno()
    mode

    對於文字檔案,檔案物件的encoding變數表示編碼格式,errors變數表示編碼或解碼錯誤的處理方式,他們均與傳遞給open函式的同名參數有關。

    對於文字檔案,檔案物件的line_buffering屬性是一個布林值,表示是否使用了行緩沖,如果為True,那麽表示使用了行緩沖。

    encoding
    errors
    line_buffering

    對於停用緩沖區的二進位檔案,檔案物件的closefd屬性表示當檔案關閉時是否同時關閉檔案描述器,這與傳遞給open函式的closefd參數有關。

    closefd

    info.py
    # …
    with open('read.txt', 'r', encoding='utf8') as file:
    	print(f'name {file.name}')
    	print(f'fileno {file.fileno()}')
    	print(f'mode {file.mode}')
    	print(f'encoding {file.encoding}')
    	print(f'errors {file.errors}')
    	print(f'line_buffering {file.line_buffering}')
    
    import os fd = os.open('read.txt', os.O_RDONLY | os.O_BINARY) with open(fd, 'rb', buffering=0, closefd=False) as file: print(f'name {file.name}') print(f'closefd {file.closefd}')
    name read.txt
    fileno 3
    mode r
    encoding utf8
    errors strict
    line_buffering False
    name 3
    fileno 3
    closefd False

    原始碼

    src/zh-hant/file_system/files/file_objects·codebeatme/python·GitHub