Python通用程式設計 - 第五章:檔案處理

Albert01發表於2019-04-04

本文是Python通用程式設計系列教程,已全部更新完成,實現的目標是從零基礎開始到精通Python程式語言。本教程不是對Python的內容進行泛泛而談,而是精細化,深入化的講解,共5個階段,25章內容。所以,需要有耐心的學習,才能真正有所收穫。雖不涉及任何框架的使用,但是會對作業系統和網路通訊進行全域性的講解,甚至會對一些開源模組和伺服器進行重寫。學完之後,你所收穫的不僅僅是精通一門Python程式語言,而且具備快速學習其他程式語言的能力,無障礙閱讀所有Python原始碼的能力和對計算機與網路的全面認識。對於零基礎的小白來說,是入門計算機領域並精通一門程式語言的絕佳教材。對於有一定Python基礎的童鞋,相信這套教程會讓你的水平更上一層樓。

一 檔案讀寫基本操作

1. 檔案操作的工作流程

檔案在我們的計算機上隨處可見,當我們需要永久儲存資料的時候就會用到檔案,檔案是由計算機作業系統來提供的,那麼自然也就受作業系統的控制。如下圖所示,一套完整的計算機系統主要由三部分構成:

  1. 應用程式
  2. 作業系統
  3. 計算機底層硬體

WechatIMG33.jpeg
如果應用程式需要操作硬體,必須先要發指令給作業系統,通過作業系統來幫應用程式完成對機器硬體的操作。如果應用程式需要把自己產生的資料永久儲存起來(應用程式產生的資料原本是在記憶體中),那麼就是把它寫入硬碟,這時應用程式要通過作業系統提供的介面來控制硬體,如果應用程式需要讀取檔案內容,同樣是向作業系統發起請求,最後由作業系統返回檔案內容,這之間的過程如下圖所示:
image.png
接下來我們要講解的就是應用程式如何呼叫作業系統提供的介面來讀取和寫入檔案。

2. 檔案操作的基本形式

我們事先在當前路徑下準備好一個檔案a.txt,如下圖所示:

image.png
接下來我們在 "檔案處理.py" 檔案內開始寫開啟讀取檔案的操作,在Python中必然會有一個功能或者介面來開啟檔案,這個介面就是open,使用引數如下圖所示:
image.png
檔案讀取操作程式碼示例如下:

"""
open開啟檔案需要3個引數,
開啟檔案之後會有一個返回值,
讀寫操作就是對這個返回值進行操作
f = open('檔案的路徑',mode='開啟檔案的模式',encoding='操作檔案的字元編碼')
"""

"""
open是向作業系統發請求,會佔用作業系統資源,這個資源不會自動回收
返回值就是應用程式拿到的變數,應用程式的變數Python直譯器會自動幫你回收
"""
# 1 開啟檔案
f = open(r'a.txt',mode='r',encoding='utf-8')
"""
對於應用程式來說上面這行程式碼與我寫一個"f = 1"沒有本質區別,
你不需要再執行"del f",因為直譯器會自動幫你清理這個應用程式資源,
但是,開啟的檔案佔用了作業系統的資源,這不會自動回收
"""

# 2 讀取檔案
data = f.read()
print(data)

# 3 關閉檔案,清理作業系統開啟檔案的資源
f.close()
print(f)  # 應用程式的資源還在
# f.read()  # 檔案關閉不能再進行讀取
複製程式碼

開啟一個檔案其實是佔用了兩部分資源,分別是作業系統資源和應用程式資源,應用程式資源會自動由Python直譯器來回收,而作業系統開啟檔案的資源並不會立即回收,作業系統每開啟一個檔案其實會有一個編號,每個編號與應用程式向作業系統發起檔案操作請求的編號一一對應,這個編號稱為檔案描述符,作業系統的檔案描述符編號是有限的,所以,當伺服器高併發的時候,由於開啟檔案個數非常多,因而還沒來得及關閉,那麼伺服器就卡了,返回給用的的結果就是使用者的客戶端卡了。

有的時候關閉檔案的操作總是會被遺忘,我們有一個使用 "with"來操作檔案的方式,它是一個上下文的操作,會幫你自動的關閉檔案,程式碼示例如下:

# as 指的是賦值
with open('a.txt', 'r', encoding='utf-8') as f:
    data = f.read()
    print(data)
複製程式碼

除此之外,“with” 可以連續開啟多個檔案,程式碼示例如下:

with open('a1.txt', 'r', encoding='utf-8') as f1, \
        open('a2.txt', 'r', encoding='utf-8') as f2:
    data1 = f1.read()
    data2 = f2.read()
複製程式碼

二 預設開啟檔案的引數說明

1. 檔案開啟的字元編碼

如果不指定字元編碼,預設開啟檔案的字元編碼與作業系統相匹配:

  • Windows系統(中國大陸使用者):gbk
  • Liunx系統:utf-8
  • MacOS:utf-8

在不指定字元編碼的情況下,MacOS系統示例程式碼:

# 檔案儲存的以utf-8編碼儲存,與本機預設編碼一致
with open('a.txt', 'r', ) as f:
    data = f.read()
    print(data)
複製程式碼

2. 檔案的開啟模式

檔案預設的開啟模式是“t”模式,指的是文字模式,這意味著在該模式下無法開啟圖片,視訊和音訊等檔案,因為這些是以二進位制格式儲存的,文字模式是以字元形式儲存的。

操作檔案的模式有三種,分別是:“r”,“w”和“a”模式,“r”是隻讀,“w”是隻寫,“a”是指追加,預設操作檔案大模式是“r”模式,所以預設檔案的開啟模式是“rt”模式,對於操作文字檔案,“t”模式必須與操作檔案的三種模式連用,很多時候你看到的,這個“t”經常會省略不寫,這是可以的。

三 文字模式開啟檔案的操作

1. 操作檔案“r”模式

全部讀取使用read,程式碼示例如下:

f = open('a.txt', mode='r', encoding='utf-8')  # “r”模式下,如果檔案不存在會報錯
# f.write('哈哈') #丟擲異常,不能寫
print(f.readable())  # 判斷是否可讀
print('=============>1')
print(f.read())  # 全部讀取
print('=============>2')
# 讀檔案會有一個游標移動,第一次讀完了,游標移至末尾,第二次讀無內容
print(f.read())
f.close()
複製程式碼

一行一行讀檔案內容使用readline,程式碼示例如下:

f = open('a.txt', mode='r', encoding='utf-8')
# readline指的是一行一行讀檔案
print(f.readline(), end='')  # 檔案中有換行,print也自帶換行,指定end引數去掉預設換行
print(f.readline(), end='')
print(f.readline(), end='')
f.close()
複製程式碼

全部讀取檔案內容,存入列表,每行內容為列表的一個元素使用readlines,程式碼示例如下:

f = open('a.txt', mode='r', encoding='utf-8')

print(f.readlines()) 
f.close()
複製程式碼

readlines可以加數字作為引數,但是他不是指的行數,而是位元組數,所以我們一般不用,如需逐行列印檔案內容常用readlines與for迴圈連用,程式碼示例如下:

# 如果檔案內容比較少的時候,以下兩種方式都可以

with open('a.txt') as f:
    # 當檔案很大時,f.readlines()結果是一個很大的列表在記憶體中,機器就卡了
    for line in f.readlines(): 
        print(line, end='')

# 推薦使用這種方式
with open('a.txt') as f:  # f是一個可迭代物件,就像老母雞會下蛋一樣 
    for line in f:  
      	# 檔案內容很大時,使用這種方式每次記憶體中只有一行內容
        print(line, end='')
複製程式碼

2. 操作檔案“w”模式

注意:在“w”只寫模式下,當檔案存在時,就會清空該檔案,程式碼示例如下:

f = open(r'a.txt', mode='w', encoding='utf-8')  # 預設是wt
f.write('第一行\n')  # 需要自己新增“\n”來換行
f.write('第二行\n')
f.close()
複製程式碼

當檔案不存在時,就會建立空文件,程式碼示例如下:

f = open(r'a1.txt', mode='w', encoding='utf-8')  # 預設是wt
f.write('第一行\n')
f.write('第二行\n')
f.close()
複製程式碼

只寫模式常用的方法:

f = open(r'a1.txt', mode='w', encoding='utf-8')  # 預設是wt

f.writable()
# writelines指的是可以放一個列表或者元組,裡面可以有多行內容,需要自己加換行符
f.writelines(['111111\n', '222222\n', '333333\n'])
# 下面這樣程式碼與上面寫的結果一樣
# f.write('aaaaaa\nbbbbbbb\ncccccc\n')
f.close()
複製程式碼

3. 操作檔案“a”模式

“a”模式指的是隻追加寫,當檔案不存在時,建立空檔案;當檔案存在時,游標直接移至檔案末尾,所以,我們在記錄日誌的時候都會使用“a”模式,程式碼示例如下:

f = open('access.log', mode='a', encoding='utf-8')
print(f.writable())
print(f.readable())
f.write('5555555555555\n')
f.close()
複製程式碼

四 二進位制模式開啟檔案的操作

1. “b”模式基本介紹

“b”模式指的是檔案開啟的模式為“b”模式, 它與“t”模式類似,不能單獨使用,必須以“rb”,“wb”或者“ab”模式來使用,“b”模式讀寫都是以bytes為單位進行的,所以可以理解為“b”模式就是二進位制模式。對於普通文字來說是以字元的形式儲存的,但是對於圖片,視訊或者音訊等等這些檔案則是以二進位制形式儲存的,所以“t”模式無法讀取,程式碼及報錯示例如下:

image.png
圖片檔案並不是以字元編碼儲存的,而是以JPG這個格式儲存成了二進位制形式,與字元編碼沒有關係,所以我們以文字模式處理檔案是不可行的。應該以二進位制模式開啟檔案,這時不需要指定字元編碼,正確的開啟方式請看如下程式碼示例:

# b模式下一定不能指定encoding引數
with open('01.jpg', 'rb', ) as f:
    data = f.read()
    print(data)
複製程式碼

2. 操作檔案的“rb”模式

需要說明的一點是,“b”模式也可以讀取文字檔案,字元的底層都是以二進位制形式儲存的,只不過你在使用“t”模式讀取文字檔案的時候open幫你把二進位制轉成了能夠看懂的文字,這是“t”模式的便利之處,但是它有侷限性,只能操作文字檔案,而“b”模式具有統一性,任何檔案底層儲存原理都是二進位制,這也就是意味著“b”模式可以操作任何檔案,程式碼示例如下:

with open('01.jpg', 'rb', ) as f1, open('a.txt', 'rb') as f2:
    img = f1.read()
    text = f2.read()
    print(text.decode('utf-8'))  # 把bytes轉化成utf-8
複製程式碼

3. 操作檔案的“wb”模式

“wb”模式也是操作檔案“w”模式的一種,當檔案存在時,就會清空該檔案,當檔案不存在時,就會建立空檔案,程式碼示例如下:

# wb模式寫入
with open('a.txt', 'wb') as f:
    msg = '你好,世界'
    f.write(msg.encode('utf-8'))  # 指定寫入檔案的字元編碼

# rb模式讀取
with open('a.txt', 'rb') as f:
    data = f.read()
    print(data)
    print(type(data))
    print(data.decode('utf-8'))  # 指定讀取檔案的字元編碼
複製程式碼

4. 操作檔案的“ab”模式

“ab”模式指的是以二進位制形式追加寫,與操作檔案的“a”模式同理,程式碼示例如下:

with open('a.txt', 'ab') as f:
    msg = '\n世界:你也好,小鬼'
    f.write(msg.encode('utf-8'))  # 指定寫入檔案的字元編碼
複製程式碼

補充: 在這這裡講一個小模組的部分用法,是sys模組,它的使用說明請看下面程式碼示例和截圖:

import sys  # 首先匯入這個模組

list_test = sys.argv  # 它的返回值是一個列表
print(list_test)
複製程式碼

接下來我們在終端環境下執行以下命令(注意引數1前後的空格):

image.png
你可以看到在終端執行列印的結果就是一個列表,第一個值是檔案路徑,第二個和第三個值分別是兩個引數。

5. 操作檔案的其他模式(瞭解)

一般來說,你遇到的操作檔案的模式都是隻讀或者只寫,但是也有可讀可寫的模式,這類模式瞭解即可,說明和程式碼示例如下:

# "r+t"模式,或者寫成"r+"模式,指的是可讀可寫
with open('a.txt', 'r+t', encoding='utf-8') as f:
    print(f.readable())
    print(f.writable())

# "w+r"或者"w+"模式,可讀可寫
with open('a.txt', 'w+t', encoding='utf-8') as f:
    print(f.readable())
    print(f.writable())

# "a+t"或者"a+",可讀可追加寫
with open('a.txt', 'a+t', encoding='utf-8') as f:
    print(f.readable())
    print(f.writable())

# "U"模式,通用換行符,已廢棄,無需瞭解
# with open('a.txt', 'U', encoding='utf-8') as f:
#     print(f.readable())
#     print(f.writable())
複製程式碼

“r+”與“w+”,“a+”模式都是可讀可寫,他們的區別在於不改變自身原本操作檔案的形式,即“r+”模式下,當檔案不存在會儲存,“w+”或者“a+”模式當檔案不存在會建立新檔案,當檔案存在會清空檔案。

五 檔案內游標移動

在開啟檔案時會有一個游標移動,我們使用seek這個方法,可以實現游標的移動,程式碼示例如下:

with open('a.txt', 'r') as f:
    f.seek(9)  # 這個引數指的是偏移量,以位元組為單位
    data = f.read()
    print(data)
複製程式碼

注意:游標移動只能是從左往右移動,seek可以加兩個引數,第一個引數就是上面程式碼中的引數,你如果只傳一個引數也就是指的這個引數,第二個引數預設是0,指的是游標在檔案開頭位置開始移動,除了0之外,只能接收1或者2作為引數,1表示從當前位置開始移動,2表示從檔案末尾開始移動,其中1和2必須在“b”模式下進行,0可以在“t”模式或者“b”下都能執行,但是無論哪種模式,都是以bytes為單位進行的,程式碼示例如下:

with open('a.txt', 'rb') as f:
    f.seek(3, 0)  # 移動三個位元組,也就是utf-8編碼下一個中文字元
    print(f.tell())  # 當前游標位置,以位元組為單位
    f.seek(3, 1)  # 從當前位置向右偏移3個位元組然後再讀取檔案內容
    # f.seek(3, 2)  # 只能讀取動態資料新新增的內容
    data = f.read()
    print(data.decode('utf-8'))

with open('a.txt', 'r') as f:
    f.seek(3, 0)
    print(f.read())
複製程式碼

補充: 在這裡補充一個小模組的用法,是os模組,它的使用說明請看下面程式碼示例:

import os  # 首先匯入這個模組

os.rename('a.txt', 'b.txt')  # 修改檔名,兩個引數分別為原始檔名和目標檔名
os.remove('a1.txt')  # 刪除a1.txt檔案,這個引數指的是檔案路徑
複製程式碼

相關文章