Python 簡明教程 --- 24,Python 檔案讀寫

碼農充電站發表於2020-07-20

微信公眾號:碼農充電站pro
個人主頁:https://codeshellme.github.io

過去的程式碼都是未經測試的程式碼。

目錄

在這裡插入圖片描述

無論是哪種程式語言,IO 操作都是非常重要的部分。IInput(輸入),OOutput(輸出)。

IO 操作一般分為以下兩種:

  • 磁碟IO: 即在磁碟上讀寫檔案。讀檔案是指將檔案內容從磁碟讀入記憶體,寫檔案是指將記憶體中的內容寫到磁碟。
  • 網路IO: 即檔案在網路上傳輸。網路傳輸一般會有兩種角色,分別是服務端(如HTTP Server)和客戶端(如瀏覽器)。

本節我們主要介紹磁碟IO,即檔案讀寫

1,open 函式介紹

要想讀寫檔案,首先要開啟一個檔案。

Python 中的內建函式open 用來開啟一個檔案,我們可以使用help(open),來檢視open 函式的原型,如下:

open(file, mode='r', 
    buffering=-1, encoding=None,
    errors=None, newline=None, 
    closefd=True, opener=None)

該函式成功呼叫時會返回一個流stream,用於讀寫檔案等操作;發生錯誤時會丟擲IOError 異常。

被開啟的檔案佔用了系統資源,使用完後要記得close,否則會浪費系統資源。

不管以讀模式開啟檔案,還是以寫模式開啟檔案,成功開啟一個檔案後,這個可操作檔案的的內部都有一個隱含的指標,一般這個指標會指向檔案開頭或者檔案末尾的位置,表示從檔案的哪個位置讀寫檔案。

可以看到,該函式支援8 個引數,但最重要的是前兩個引數:

  • file:是指要開啟的檔案的路徑
  • mode:是指以什麼模式開啟檔案,要用引號引住

mode 引數支援的模式(預設為讀文字模式,即rt)如下:

  • r:以讀模式開啟檔案(預設方式),指標在檔案開頭
  • w:以寫模式開啟檔案,如果件已存在,則內容會被清空(指標在檔案開頭);如果檔案不存在,則會建立新檔案
  • x:建立一個新檔案,並以寫模式開啟,指標在檔案開頭,如果檔案已存在,則丟擲FileExistsError異常
  • a:以寫模式開啟檔案,如果檔案已有內容,在寫入內容時,會追加到檔案末尾(指標在檔案末尾)
  • b:以二進位制模式開啟檔案,一般用於讀寫二進位制檔案,如圖片,視訊等
  • t:以文字模式開啟檔案(預設方式),一般用於讀寫文字檔案
  • +:以讀寫模式開啟檔案,指標在檔案開頭

這些模式還可以組合使用,常見的組合如下:

  • rb:以二進位制模式開啟一個檔案,用於只讀
  • r+:開啟一個檔案,用於讀寫
  • rb+:以二進位制模式開啟一個檔案,用於讀寫
  • wb:以二進位制模式開啟一個檔案,用於
  • w+:開啟一個檔案,用於讀寫
  • wb+: 以二進位制模式開啟一個檔案,用於讀寫
  • ab: 以二進位制模式開啟一個檔案,用於追加
  • a+:開啟一個檔案用於讀寫,指標在檔案末尾
  • ab+:以二進位制模式開啟一個檔案,用於讀寫,指標在檔案末尾

2,open 函式示例

如下程式碼,成功開啟檔案./1.txt

f = open('./1.txt')

通過type(f)檢視open 函式的返回值的型別:

>>> type(file)
<class '_io.TextIOWrapper'>

可看到,其返回值型別為_io.TextIOWrapper

我們用dir(f) 來檢視物件 f 支援的屬性和方法:

>>> dir(file)
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', 
'__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', 
'__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', 
'__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', 
'__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', 
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
'_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', 
'_finalizing', 
'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 
'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 
'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 
'truncate', 'writable', 'write', 'writelines']

可以通過help(f.方法名) 來檢視每個方法的幫助手冊,也可以使用help(f) 來檢視該物件的所有屬性和方法,及其簡介。

我們來看一下常用方法的作用:

  • mode:開啟檔案時的模式
  • name:被開啟的檔名
  • close:關閉檔案流,並重新整理緩衝區中的內容,之後不能再操作檔案
  • closed:檔案流是否已關閉
  • flush:重新整理寫緩衝區,只寫流非阻塞流不適用
  • read:讀入檔案內容
  • readable:是否可讀
  • readline:讀入一行內容
  • readlines:讀入檔案所有的行,直至檔案末尾
  • seek:移動檔案指標的位置
  • seekable:檔案指標是否可被移動
  • tell:返回檔案指標當前位置
  • truncate:截斷檔案內容
  • writable:是否可寫
  • write:向檔案中寫入內容
  • writelines:向檔案中寫入多行

3,關閉系統資源

正確的呼叫close() 函式是關鍵的。

在成功開啟一個檔案後,對該檔案進行操作(讀寫)時,有可能發生異常。

比如我們開啟的檔案只能用來,如果用來,則會發生異常:

>>> f = open('1.txt', 'w')  # 用只讀模式開啟檔案
>>> f.readable()            # 檢視檔案是否可讀
False                       # 返回 False,表示不可讀
>>> f.read()                # 讀檔案,發生異常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
io.UnsupportedOperation: not readable

如果,我們將這段程式碼寫在檔案中:

#! /usr/bin/env python3

f = open('1.txt', 'w')
f.read()
f.close()

python3 來執行,結果如下:

$ python3 Test.py 
Traceback (most recent call last):
  File "Test.py", line 4, in <module>
    f.read()
io.UnsupportedOperation: not readable

可以看到,在執行到f.read() 這句程式碼的時候,程式異常退出,那麼後邊的f.close() 就沒有執行到,這就導致程式執行不夠完整,系統資源沒有關閉。

這時,我們可以用try...finally來處理,如下:

#! /usr/bin/env python3

f = open('1.txt', 'w')

try:
    f.read()

except Exception as e:
    print('read file err:%s' % e)

finally:
    f.close()
    print('file closed')

上面程式碼的執行結果如下:

$ python3 Test.py 
read file err:not readable
file closed

我們將f.close() 這句程式碼放在了finally 程式碼塊中,這樣,不管遇到什麼情況,f.close() 這句話總會被執行,就不會導致系統資源洩漏的問題。

4,with 語句使用

為了確保系統資源能夠關閉,Python 中提供了with 語句,能夠讓我們更加安全方面的使用open 函式,而不用關心資源關閉的問題。

with 語句也叫上下文管理器,有了with 語句,我們可以這樣使用open 函式:

with open('./1.txt') as f:
    print(f.read())

這樣的程式碼,不管在with 語句塊內出現怎樣的異常,close 函式都會被呼叫,而我們也不需要自己呼叫。

使用with 語句,就不再需要使用try...finally 語句,也使得程式碼更加簡潔。

需要特別注意的是,這裡的f只能在with 語句塊中使用,一旦離開with 語句塊,f 就被關閉了。如果在with 語句塊之外使用f 進行讀寫等操作,將出現異常。

如下程式碼中,f.closed 將返回True

with open('./1.txt') as f:
    pass
f.closed  # True

5,with 語句原理

為什麼open 函式能夠使用with 語句?

實際上open 函式能夠使用with 語句的原因取決於open 的返回值的型別。我們知道,open 的返回值的型別為_io.TextIOWrapper,而這個類中有兩個方法,__enter__ 方法和__exit__ 方法。

我們再來看下with 語句的格式:

with ... as ... :
    pass

with 關鍵字的後邊是一個表示式as 後邊是一個變數名,表示式的計算結果會賦值給as 後邊的變數。

Python 規定,只要一個類中有__enter____exit__ 方法,就可以使用with 語句。with 語句後邊的表示式執行完畢後,就會執行__enter__ 方法,在退出with 語句塊時,會執行__exit__ 方法。

我們自己編寫一個測試類,使其能夠使用with 語句:

#! /usr/bin/env python3

class TestWith:

    def __init__(self):
        print('執行__init__')

    def __enter__(self):
        print('執行__enter__')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('執行__exit__')

        print('exc_type is %s' % exc_type)
        print('exc_val is %s' % exc_val)
        print('exc_tb is %s' % exc_tb)

再該類中有三個函式:

  • __init__:建構函式,建立類的物件時呼叫
  • __enter__:進入with 語句塊時會呼叫
  • __exit__:離開with 語句塊時會呼叫

其中__exit__ 方法有三個引數:

  • exc_typewith 語句塊中的程式碼發生異常時的異常型別
  • exc_val:發生異常時的異常值
  • exc_tb:發生異常時的traceback 類的物件

我們這樣使用這個類:

with TestWith() as t:
    print('test with')

python3 來執行,結果如下:

$ python3 Test.py 
執行__init__
執行__enter__
test with
執行__exit__
exc_type is None
exc_val is None
exc_tb is None

可以看到執行步驟是這樣的:

  1. 生成該類的物件,執行__init__ 方法
  2. 進入with 語句塊,執行__enter__ 方法
  3. 執行with 語句塊中的程式碼
  4. 退出with 語句塊,執行__exit__ 方法

因為with 語句塊中沒有發生異常,所以__exit__ 方法中的 exc_typeexc_valexc_tb 三個引數均為None

下面再示範一個with 語句塊中出現異常的程式碼:

with TestWith() as t:
    print('test with1...')
    1 / 0   # 除數為 0,丟擲異常
    print('test with2...')

該程式碼的執行結果如下:

$ python3 Test.py 
執行__init__
執行__enter__
test with1...
執行__exit__
exc_type is <class 'ZeroDivisionError'>
exc_val is division by zero
exc_tb is <traceback object at 0x7fe8b7c98888>
Traceback (most recent call last):
  File "Test.py", line 27, in <module>
    1 / 0
ZeroDivisionError: division by zero

通過上面的執行結果可以看到,在執行1 / 0 之前,我們不用多說。在執行到1 / 0 時,出現異常,然後會執行__exit__ 方法。

在執行結果中,我們能看到 exc_typeexc_valexc_tb 三個引數的值,最後丟擲Traceback 異常。

with 語句中,丟擲異常的語句1 / 0 之後的程式碼不會再執行。

(完。)


推薦閱讀:

Python 簡明教程 --- 19,Python 類與物件

Python 簡明教程 --- 20,Python 類中的屬性與方法

Python 簡明教程 --- 21,Python 繼承與多型

Python 簡明教程 --- 22,Python 閉包與裝飾器

Python 簡明教程 --- 23,Python 異常處理


歡迎關注作者公眾號,獲取更多技術乾貨。

碼農充電站pro

相關文章