1. 程式人生 > >Python 簡明教程 --- 24,Python 檔案讀寫

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

> **微信公眾號:碼農充電站pro** > **個人主頁:** > **過去的程式碼都是未經測試的程式碼。** **目錄** ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200720110122608.png?#center) 無論是哪種程式語言,`IO` 操作都是非常重要的部分。`I` 即`Input`(輸入),`O` 即`Output`(輸出)。 `IO` 操作一般分為以下兩種: - **磁碟IO:** 即在磁碟上`讀寫`檔案。`讀檔案`是指將檔案內容從磁碟讀入記憶體,`寫檔案`是指將記憶體中的內容寫到磁碟。 - **網路IO:** 即檔案在網路上傳輸。網路傳輸一般會有兩種角色,分別是`服務端`(如`HTTP Server`)和`客戶端`(如`瀏覽器`)。 本節我們主要介紹`磁碟IO`,即`檔案讀寫`。 ### 1,`open` 函式介紹 要想讀寫檔案,首先要`開啟`一個檔案。 Python 中的內建函式`open` 用來開啟一個檔案,我們可以使用`help(open)`,來檢視`open` 函式的原型,如下: ```python 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`: ```shell f = open('./1.txt') ``` 通過`type(f)`檢視`open` 函式的返回值的型別: ```shell >
>> type(file) ``` 可看到,其返回值型別為`_io.TextIOWrapper`。 我們用`dir(f)` 來檢視`物件 f` 支援的屬性和方法: ```shell >>> 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()` 函式是關鍵的。 在成功開啟一個檔案後,對該檔案進行操作(讀寫)時,有可能發生異常。 比如我們開啟的檔案只能用來`寫`,如果用來`讀`,則會發生異常: ```shell >>> f = open('1.txt', 'w') # 用只讀模式開啟檔案 >>> f.readable() # 檢視檔案是否可讀 False # 返回 False,表示不可讀 >>> f.read() # 讀檔案,發生異常 Traceback (most recent call last): File "", line 1, in io.UnsupportedOperation: not readable ``` 如果,我們將這段程式碼寫在檔案中: ```python #! /usr/bin/env python3 f = open('1.txt', 'w') f.read() f.close() ``` 用`python3` 來執行,結果如下: ```shell $ python3 Test.py Traceback (most recent call last): File "Test.py", line 4, in f.read() io.UnsupportedOperation: not readable ``` 可以看到,在執行到`f.read()` 這句程式碼的時候,程式異常退出,那麼後邊的`f.close()` 就沒有執行到,這就導致程式執行不夠完整,系統資源沒有關閉。 這時,我們可以用`try...finally`來處理,如下: ```python #! /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') ``` 上面程式碼的執行結果如下: ```shell $ python3 Test.py read file err:not readable file closed ``` 我們將`f.close()` 這句程式碼放在了`finally` 程式碼塊中,這樣,不管遇到什麼情況,`f.close()` 這句話總會被執行,就不會導致系統資源洩漏的問題。 ### 4,`with` 語句使用 為了確保系統資源能夠關閉,Python 中提供了`with` 語句,能夠讓我們更加安全方面的使用`open` 函式,而不用關心資源關閉的問題。 `with` 語句也叫`上下文管理器`,有了`with` 語句,我們可以這樣使用`open` 函式: ```python with open('./1.txt') as f: print(f.read()) ``` 這樣的程式碼,不管在`with` 語句塊內出現怎樣的異常,`close` 函式都會被呼叫,而我們也不需要自己呼叫。 使用`with` 語句,就不再需要使用`try...finally` 語句,也使得程式碼更加簡潔。 需要特別注意的是,這裡的`f`只能在`with` 語句塊中使用,一旦離開`with` 語句塊,`f` 就被關閉了。如果在`with` 語句塊之外使用`f` 進行讀寫等操作,將出現異常。 如下程式碼中,`f.closed` 將返回`True`: ```python with open('./1.txt') as f: pass f.closed # True ``` ### 5,`with` 語句原理 為什麼`open` 函式能夠使用`with` 語句? 實際上`open` 函式能夠使用`with` 語句的原因取決於`open` 的返回值的`型別`。我們知道,`open` 的返回值的型別為`_io.TextIOWrapper`,而這個類中有兩個方法,`__enter__` 方法和`__exit__` 方法。 我們再來看下`with` 語句的格式: ```python with ... as ... : pass ``` `with` 關鍵字的後邊是一個`表示式`,`as` 後邊是一個變數名,表示式的計算結果會賦值給`as` 後邊的變數。 Python 規定,只要一個類中有`__enter__`和`__exit__` 方法,就可以使用`with` 語句。`with` 語句後邊的表示式執行完畢後,就會執行`__enter__` 方法,在退出`with` 語句塊時,會執行`__exit__` 方法。 我們自己編寫一個測試類,使其能夠使用`with` 語句: ```python #! /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_type`:`with` 語句塊中的程式碼發生異常時的`異常型別` - `exc_val`:發生異常時的`異常值` - `exc_tb`:發生異常時的`traceback` 類的物件 我們這樣使用這個類: ```python with TestWith() as t: print('test with') ``` 用`python3` 來執行,結果如下: ```shell $ 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_type`,`exc_val`,`exc_tb` 三個引數均為`None`。 下面再示範一個`with` 語句塊中出現異常的程式碼: ```python with TestWith() as t: print('test with1...') 1 / 0 # 除數為 0,丟擲異常 print('test with2...') ``` 該程式碼的執行結果如下: ```shell $ python3 Test.py 執行__init__ 執行__enter__ test with1... 執行__exit__ exc_type is exc_val is division by zero exc_tb is Traceback (most recent call last): File "Test.py", line 27, in 1 / 0 ZeroDivisionError: division by zero ``` 通過上面的執行結果可以看到,在執行`1 / 0` 之前,我們不用多說。在執行到`1 / 0` 時,出現異常,然後會執行`__exit__` 方法。 在執行結果中,我們能看到 `exc_type`,`exc_val`,`exc_tb` 三個引數的值,最後丟擲`Traceback` 異常。 `with` 語句中,丟擲異常的語句`1 / 0` 之後的程式碼不會再執行。 (完。) --- **推薦閱讀:** [Python 簡明教程 --- 19,Python 類與物件](https://www.cnblogs.com/codeshell/p/13193866.html) [Python 簡明教程 --- 20,Python 類中的屬性與方法](https://www.cnblogs.com/codeshell/p/13197968.html) [Python 簡明教程 --- 21,Python 繼承與多型](https://www.cnblogs.com/codeshell/p/13233821.html) [Python 簡明教程 --- 22,Python 閉包與裝飾器](https://www.cnblogs.com/codeshell/p/13237874.html) [Python 簡明教程 --- 23,Python 異常處理](https://www.cnblogs.com/codeshell/p/13332048.html) --- 歡迎關注作者公眾號,獲取更多技術乾貨。 ![碼農充電站pro](https://img-blog.csdnimg.cn/20200505082843773.png?#pic