檔案是無處不在的,無論我們使用哪種程式設計語言,處理檔案對于每個程式員都是必不可少的
檔案處理是一種用于建立檔案、寫入資料和從中讀取資料的過程,Python 擁有豐富的用于處理不同檔案類型的包,進而使得我們可以更加輕松友善的完成檔案處理的工作
本文大綱:
- 使用上下文管理器打開檔案
- Python 中的檔案讀取模式
- 讀取 text 檔案
- 讀取 CSV 檔案
- 讀取 JSON 檔案
Let's go!
打開檔案
在通路檔案的内容之前,我們需要打開檔案。Python 提供了一個内置函數可以幫助我們以不同的模式打開檔案。
open()
函數接受兩個基本參數:檔案名和模式
預設模式是“r”,它以隻讀方式打開檔案。這些模式定義了我們如何通路檔案以及我們如何操作其内容。
open()
函數提供了幾種不同的模式,我們将在後面逐一讨論
下面我們通過 ’Python 之禅‘ 檔案來進行後面的讨論學習
f = open('zen_of_python.txt', 'r')
print(f.read())
f.close()
Output:
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
...
在上面的代碼中,
open()
函數以隻讀模式打開文本檔案,這允許我們從檔案中擷取資訊而不能更改它。在第一行,
open()
函數的輸出被指派給一個代表文本檔案的對象
f
,在第二行中,我們使用
read()
方法讀取整個檔案并列印其内容,
close()
方法在最後一行關閉檔案。需要注意,我們必須始終在處理完打開的檔案後關閉它們以釋放我們的計算機資源并避免引發異常
在 Python 中,我們可以使用
with
上下文管理器來確定程式在檔案關閉後釋放使用的資源,即使發生異常也是如此
with open('zen_of_python.txt') as f:
print(f.read())
Output:
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
...
上面的代碼使用
with
語句建立了一個上下文,并綁定到變量
f
,所有檔案對象方法都可以通過該變量通路檔案對象。
read()
方法在第二行讀取整個檔案,然後使用
print()
函數輸出檔案内容
當程式到達 with 語句塊上下文的末尾時,它會關閉檔案以釋放資源并確定其他程式可以正常調用它們。通常當我們處理不再需要使用的,需要立即關閉的對象(例如檔案、資料庫和網絡連接配接)時,強烈推薦使用 with 語句
這裡需要注意的是,即使在退出 with 上下文管理器塊之後,我們也可以通路
f
變量,但是該檔案是已關閉狀态。讓我們嘗試一些檔案對象屬性,看看變量是否仍然存在并且可以通路:
print("Filename is '{}'.".format(f.name))
if f.closed:
print("File is closed.")
else:
print("File isn't closed.")
Output:
Filename is 'zen_of_python.txt'.
File is closed.
但是此時是不可能從檔案中讀取内容或寫入檔案的,關閉檔案時,任何通路其内容的嘗試都會導緻以下錯誤:
f.read()
Output:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_9828/3059900045.py in <module>
----> 1 f.read()
ValueError: I/O operation on closed file.
Python 中的檔案讀取模式
正如我們在前面提到的,我們需要在打開檔案時指定模式。下表是 Python 中的不同的檔案模式:
模式說明
- 'r' 打開一個隻讀檔案
- 'w' 打開一個檔案進行寫入。如果檔案存在,會覆寫它,否則會建立一個新檔案
- 'a' 打開一個僅用于追加的檔案。如果該檔案不存在,會建立該檔案
- 'x' 建立一個新檔案。如果檔案存在,則失敗
- '+' 打開一個檔案進行更新
我們還可以指定以文本模式“t”、預設模式或二進制模式“b”打開檔案。讓我們看看如何使用簡單的語句複制圖像檔案 dataquest_logo.png:
with open('dataquest_logo.png', 'rb') as rf:
with open('data_quest_logo_copy.png', 'wb') as wf:
for b in rf:
wf.write(b)
上面的代碼複制 Dataquest 徽标圖像并将其存儲在同一路徑中。'rb' 模式以二進制模式打開檔案并進行讀取,而 'wb' 模式以文本模式打開檔案以并行寫入
讀取文本檔案
在 Python 中有多種讀取文本檔案的方法,下面我們介紹一些讀取文本檔案内容的有用方法
到目前為止,我們已經了解到可以使用
read()
方法讀取檔案的全部内容。如果我們隻想從文本檔案中讀取幾個位元組怎麼辦,可以在
read()
方法中指定位元組數。讓我們嘗試一下:
with open('zen_of_python.txt') as f:
print(f.read(17))
Output:
The Zen of Python
上面的簡單代碼讀取 zen_of_python.txt 檔案的前 17 個位元組并将它們列印出來
有時一次讀取一行文本檔案的内容更有意義,在這種情況下,我們可以使用 readline() 方法
with open('zen_of_python.txt') as f:
print(f.readline())
Output:
The Zen of Python, by Tim Peters
上面的代碼傳回檔案的第一行,如果我們再次調用該方法,它将傳回檔案中的第二行等,如下:
with open('zen_of_python.txt') as f:
print(f.readline())
print(f.readline())
print(f.readline())
print(f.readline())
Output:
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
這種有用的方法可以幫助我們以增量方式讀取整個檔案。
以下代碼通過逐行疊代來輸出整個檔案,直到跟蹤我們正在讀取或寫入檔案的位置的檔案指針到達檔案末尾。當
readline()
方法到達檔案末尾時,它傳回一個空字元串
with open('zen_of_python.txt') as f:
line = f.readline()
while line:
print(line, end='')
line = f.readline()
Output:
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
上面的代碼在
while
循環之外讀取檔案的第一行并将其配置設定給
line
變量。在
while
循環中,它列印存儲在
line
變量中的字元串,然後讀取檔案的下一行。
while
循環疊代該過程,直到
readline()
方法傳回一個空字元串。空字元串在
while
循環中的計算結果為
False
,是以疊代過程終止
讀取文本檔案的另一個有用方法是
readlines()
方法,将此方法應用于檔案對象會傳回包含檔案每一行的字元串清單
with open('zen_of_python.txt') as f:
lines = f.readlines()
讓我們檢查 lines 變量的資料類型,然後列印它:
print(type(lines))
print(lines)
Output:
<class 'list'>
['The Zen of Python, by Tim Peters\n', '\n', 'Beaut...]
它是一個字元串清單,其中清單中的每個項目都是文本檔案的一行,``\n` 轉義字元表示檔案中的新行。此外,我們可以通過索引或切片操作通路清單中的每個項目:
print(lines)
print(lines[3:5])
print(lines[-1])
Output:
['The Zen of Python, by Tim Peters\n', '\n', 'Beautiful is better than ugly.\n', ... -- let's do more of those!"]
['Explicit is better than implicit.\n', 'Simple is better than complex.\n']
Namespaces are one honking great idea -- let's do more of those!
讀取 CSV 檔案
到目前為止,我們已經學會了如何使用正常文本檔案。但是有時資料采用 CSV 格式,資料專業人員通常會檢索所需資訊并操作 CSV 檔案的内容
接下來我們将使用 CSV 子產品,CSV 子產品提供了有用的方法來讀取存儲在 CSV 檔案中的逗号分隔值。我們現在就嘗試一下
import csv
with open('chocolate.csv') as f:
reader = csv.reader(f, delimiter=',')
for row in reader:
print(row)
Output:
['Company', 'Bean Origin or Bar Name', 'REF', 'Review Date', 'Cocoa Percent', 'Company Location', 'Rating', 'Bean Type', 'Country of Origin']
['A. Morin', 'Agua Grande', '1876', '2016', '63%', 'France', '3.75', 'Â\xa0', 'Sao Tome']
['A. Morin', 'Kpime', '1676', '2015', '70%', 'France', '2.75', 'Â\xa0', 'Togo']
['A. Morin', 'Atsane', '1676', '2015', '70%', 'France', '3', 'Â\xa0', 'Togo']
['A. Morin', 'Akata', '1680', '2015', '70%', 'France', '3.5', 'Â\xa0', 'Togo']
...
CSV 檔案的每一行形成一個清單,其中每個項目都可以輕松的被通路,如下所示:
import csv
with open('chocolate.csv') as f:
reader = csv.reader(f, delimiter=',')
for row in reader:
print("The {} company is located in {}.".format(row[0], row[5]))
Output:
The Company company is located in Company Location.
The A. Morin company is located in France.
The A. Morin company is located in France.
The A. Morin company is located in France.
The A. Morin company is located in France.
The Acalli company is located in U.S.A..
The Acalli company is located in U.S.A..
The Adi company is located in Fiji.
...
很多時候,使用列的名稱而不是使用它們的索引,這通常對專業人員來說更友善。在這種情況下,我們不使用
reader()
方法,而是使用傳回字典對象集合的
DictReader()
方法
import csv
with open('chocolate.csv') as f:
dict_reader = csv.DictReader(f, delimiter=',')
for row in dict_reader:
print("The {} company is located in {}.".format(row['Company'], row['Company Location']))
Output:
The A. Morin company is located in France.
The A. Morin company is located in France.
The A. Morin company is located in France.
The A. Morin company is located in France.
The Acalli company is located in U.S.A..
The Acalli company is located in U.S.A..
The Adi company is located in Fiji.
...
讀取 JSON 檔案
我們主要用于存儲和交換資料的另一種流行檔案格式是 JSON,JSON 代表 JavaScript Object Notation,允許我們使用逗号分隔的鍵值對存儲資料
接下來我們将加載一個 JSON 檔案并将其作為 JSON 對象使用,而不是作為文本檔案,為此我們需要導入 JSON 子產品。然後在
with
上下文管理器中,我們使用了屬于 json 對象的
load()
方法,它加載檔案的内容并将其作為字典存儲在上下文變量中。
import json
with open('movie.json') as f:
content = json.load(f)
print(content)
Output:
{'Title': 'Bicentennial Man', 'Release Date': 'Dec 17 1999', 'MPAA Rating': 'PG', 'Running Time min': 132, 'Distributor': 'Walt Disney Pictures', 'Source': 'Based on Book/Short Story', 'Major Genre': 'Drama', 'Creative Type': 'Science Fiction', 'Director': 'Chris Columbus', 'Rotten Tomatoes Rating': 38, 'IMDB Rating': 6.4, 'IMDB Votes': 28827}
讓我們檢查内容變量的資料類型:
print(type(content))
Output:
<class 'dict'>
它的資料類型是字典,是以我們可以友善的從中提取資料
print('{} directed by {}'.format(content['Title'], content['Director']))
Output:
Bicentennial Man directed by Chris Columbus
總結
今天我們讨論了 Python 中的檔案處理,重點是讀取檔案的内容。我們了解了
open()
内置函數、
with
上下文管理器,以及如何讀取文本、CSV 和 JSON 等常見檔案類型。