1. 程式人生 > >Python入門基礎(7)——檔案讀取

Python入門基礎(7)——檔案讀取

序言:

1、不同程式語言讀寫檔案的操作步驟基本上是一致,大致可以分為以下幾個步驟:

(1)開啟檔案,獲取檔案描述符

(2)針對操作檔案描述符進行操作——讀/寫

(3)關閉檔案

2、值得注意的是,讀寫檔案操作完成以後,要及時關閉(和查詢資料庫連結是一致),雖然當前計算機,即便你不關閉也會產生宕機,但是及時關閉連結,是一個好習慣,原因分析一下:

  • 檔案物件會佔據作業系統的資源,你不關閉,那麼會出現卡頓(當然對於目前計算機來說,不會明顯)
  • 作業系統對同一時間能開啟的檔案描述符的數量是有限制的(也就是你同時開啟檔案的數量是有限制的)
  • 如果不及時關閉檔案,還可能會造成資料丟失,因為我們吧資料寫入檔案時,作業系統不會立刻吧資料寫入磁碟,而是先把資料放到記憶體緩衝區,非同步寫入磁碟,當你關閉連結的時候,作業系統會保證把沒有寫入磁碟的資料全部寫入到磁碟中,否則可能會丟失資料

3、文字內容是:

趙錢孫李,周吳鄭王。
馮陳褚衛,蔣沈韓楊。
朱秦尤許,何呂施張。
孔曹嚴華,金魏陶姜。

一、讀寫模型

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

上面寫的程式碼,是一個開啟檔案的大致函式模型,open是用來開啟檔案的,裡面有很多引數,當然我們自己寫程式碼的時候,很多引數是沒有必要寫的(一般都是些前面兩個引數)。

上面最重要的引數只有前兩個:第一個file(指定了檔案路徑,其實就是一個字串);第二個mode(模型,分為只讀,只寫,可讀可寫,,,,,,)



上面的框框是所有mode形式,一般情況下,都是直接用前面兩種形式就可以了(r和w),要注意的是:

  • 用w模式寫的時候,會將原文字內容清空,如果你想在原文字末尾寫入內容,就要用a模式
  • r+會覆蓋原來的內容,比如你原來的檔案內容是“你好啊,朋友!”,寫入“上帝”後,就會變成“上帝啊,朋友!”,用上帝覆蓋住了你好
  • a+增加了可讀性,但是其寫入內容的時候,不管你的檔案指標在哪裡都會在文字末尾寫入

1、簡單讀寫框架

(1)讀寫流程

知道了模型框架,那我們就先讀一個檔案:

# 第一步:(以只讀模式)開啟檔案
f = open('song.txt', 'r', encoding='utf-8')

# 第二步:讀取檔案內容
print(f.read())    #將讀取的檔案內容打印出來,這裡輸出的格式和原檔案內容格式一模一樣(無論是換行還是空格)

# 第三步:關閉檔案
f.close()

注意:這裡open開啟之後,讀取檔案,直接就轉換成了Unicode字串,這裡的read()方法,會一次性將檔案中所有的內容全部載入到記憶體中,這對於大部分情況是不合理的,比如一個檔案非常大,我們一次性讀取出來,就會把記憶體全部佔滿,那就沒有什麼意義了,下面介紹幾種讀檔案的方式:


上面介紹了幾乎所有的讀檔案方法,還有兩個與檔案指標位置相關的方法:

  • seek(n)————將檔案指標移動到指定位元組的位置,具體函式可參看:seek用法
  • tell()————獲取當前檔案指標所在位元組位置

(2)try .....finally......(better)

     在實現基本功能的前提下,考慮一些可能的意外因素。因為檔案讀寫時都有可能產生IO錯誤(IOError),一旦出錯,後面包括f.close()在內的所有程式碼都不會執行了。因此我們要保證檔案無論如何都能被關閉。那麼可以用try...finally來實現,這實際上就是try...except..finally的簡化版:

try:
    f = open('txt/test.txt', 'r', encoding='utf-8')
    print(f.read())
    num = 10 / 0
finally:   #目的是無論上面有沒有錯,都要將open關掉
    print('>>>>>>finally')
    if f:    #f為真,表示上面打開了檔案,所以下面要關掉
        f.close()

輸出結果:


從上面這個圖上可以看出,雖然報了錯,但是最終還是輸出了:》》》》finally,這就說明最後的程式碼塊被執行了,也就是關閉了f,

(3)with,,,,(best!)

直接上程式碼例項:

with open('txt/test.txt', 'r', encoding='utf-8') as f:     這裡是賦值給f,當with結束以後,自動關閉
    print(f.read())
print(f.closed)
結果顯示:

可以看到最後輸出了true,表示f已經關閉了

為了避免忘記或者為了避免每次都要手動關閉檔案,我們可以使用with語句(一種語法糖,語法糖是為了簡化某些操作而設計的),with語句會在其程式碼塊執行完畢之後自動關閉檔案,就如上面寫的一樣。

注意:如果with程式碼塊中出現了問題,那麼這個問題就會被丟擲,需要我們自己處理(會中斷程式碼塊的執行),所以建議在必要的時候,在with語句外面套上一層try。。。except來捕獲和處理異常:

with open('txt/test.txt', 'r', encoding='utf-8') as f:
    print(f.read())
    num = 10 / 0     #程式碼塊裡面的錯誤之處

結果顯示:


對於這個結果,其實我感覺有些疑惑,根據程式碼應該是先將所有語句輸出了,然後再顯示錯誤才對啊,這個問題留作以後再解惑,如果有知道的讀者,可以在評論回答一下,絕對重謝。

2、讀例項

之前,把所有準備工作都做好了,接下來開始乾貨了,直接上相關程式碼例項。

(1)讀取指定長度的內容:

with open('txt/test.txt', 'r', encoding='utf-8') as f:
    print(f.read(12))     #數字12就是指定長度,與編碼方式無關,就是返回12個字元

輸出:


注意:在這裡Python2與Python3還是有差別的,Python2中,數字12指的是位元組數,這就與編碼方式有關了,在一些編碼方式中,一個漢字佔3個位元組,所以在Python2中,會輸出4個漢字

(2)讀取檔案的一行內容:

with open('txt/test.txt', 'r', encoding='utf-8') as f:
    print("輸出第一行內容:"+f.readline())
    print("輸出第一行第一個字的內容:" + f.readline(1))

輸出結果:


好吧,上面顯示的結果和我預期有點不一樣:我之前以為是輸出的也是第一行內容,沒想到轉到第二行去了,後來才發現,原來文字讀取的時候,是有讀取位置的,第一行已經讀取完畢了,再次讀取的時候,不會重新開始讀,而是會在原有基礎上進行讀取。這也就是為什麼上面第二個輸出會顯示“馮”,而不是“趙”;本來想改一下,後來想想算了,當做警醒吧。

(3)遍歷檔案中的每一行

第一種方法是直接一次性讀取所有行到記憶體,然後遍歷列印:

with open('txt/test.txt', 'r', encoding='utf-8') as f:
    for line in f.readlines():
        print(line)

結果顯示:


有一個問題:就是每行文件輸出之間有一行空白區間,這個問題是因為讀取的檔案中每一行都預設有換行符,而print()方法也會輸出換行,因此兩個換行就會多出一個換行符(因為只需要1個就可以了),去掉空行也比較簡單:

  • line.rstrip()——去除字串右邊的換行符(也就是從文字的角度去除)
  • print(line,end = '')————避免print方法造成的換行

輸出結果顯示:


看,空白行被去除了

第二種方法:通過迭代器一行行讀取並列印

with open('txt/test.txt', 'r', encoding='utf-8', newline='') as f:
    for line in f:       #唯一的區別就在這裡,迭代器上一次已經說過了,當你不用的時候,它不會讀取,只有用的時候才會讀取
        print(line,end = '')    #這次去除一波換行符

顯示結果和上面是一樣的,同樣有一行空白區域

3、檔案讀取的一些其他方法


4、寫例項

原文字內容:


(1)簡單的寫入文字一句話

#寫操作
with open('Test.txt', 'w', encoding='utf-8') as f:     #這裡定義的是w模式
    print(f.write('你好'))

上面定義的是最簡單寫操作,但是注意這裡用的是w模式,操作執行的時候,首先會把原來的內容清空,然後在寫入,所以原本寫有4行百家姓的文字,變成了只有“你好”的文字:


(2)在原有文字後面新增內容

#寫操作
with open('Test.txt', 'a', encoding='utf-8') as f:
    print(f.write('你好'))

這裡以a模式進行寫入,結果就在“你好”之後,再次寫入了“你好”(因為直接寫入以後,沒有進行更改,所以文字就不再是百家姓了):


(3)換行寫入

第一種方法:從文字最後位置著手

因為a模式是追加在文字最後一行的內容,但是這裡的最後一行,你是可以控制的,比如:


比如上面這個Test文字,只有4行,那麼最後寫入以後,就會在第4行後面新增,同樣是上面的程式碼執行以後:


然後,你更改一下文字,讓最後一行是空格:


這樣,最後的位置就變成了第5行,而第5行是空白字元,如果再次執行上面的程式碼,你就會發現結果不一樣了:


所以通過更改檔案“最後位置”可以實現換行輸入,這裡你要深入理解文字的最後一行是指什麼意思

第二種方法:從程式碼入手(best)

#寫操作
with open('Test.txt', 'a', encoding='utf-8') as f:
    print(f.write('\n'+'你好'))     #直接加入換行符,簡單高效

(4)在指定位置插入

#第一種方法:在第一行周前面插入666
with open('Test.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()     #讀取檔案

print("讀取的文字:")
print(lines)

lines[0]=lines[0].replace("周","666周")  #在第一行字串裡面的周前面加入666
s=''.join(lines)

with open('Test.txt', 'w+', encoding='utf-8') as f:
    f.write(s)
del lines[:]                      #清空列表
print("最後的文字:")
print(lines)

上面這種方法是取巧的方法,先將文字讀取出來,轉換成列表,然後對每一行(字串形式)進行更改,最關鍵的字串的更改那裡,可以參考部落格:更改字串內容

上面這種方法的弊端就是,當檔案比較大的時候,會很麻煩,當然瞭如果更改內容比較少(你可以針對某些內容進行修改,讀取一部分內容,然後針對這一部分內容進行修改,然後在寫回去)

#第二種方法:插入文字到指定位置
import os

with open("Test.txt","r",encoding="utf-8") as file:
    content = file.read()
    content_add = "我加在這裡了!"
    pos = content.find("周")
    with open("Test.txt", "w", encoding="utf-8") as f:
        if pos != -1:
            content = content[:pos] +content_add+content[pos:]
            f.write(content)

上面這種方法,比第一種方法本質上是一樣的,都是直接操作字串,但是一個是replace操作,一個是連線操作,感覺連線操作更好些一下

(5)讀取一個檔案的內容,然後寫入另一個檔案中

with open("Test.txt","r",encoding="utf-8") as file:
    content = file.read()

    with open("a.txt", "w", encoding="utf-8") as f:
        f.write(content)

這個就比較簡單了,這裡不再詳細細說了。

5、二進位制檔案讀取

之前預設讀取的檔案都是文字檔案,想要讀取或者寫入二進位制檔案,只需要將“r”改成“rb”,其他模式也是這樣,只需加入一個b即可。

f = open('EDC.jpg', 'rb')
print(f.read())
# 輸出 '\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六進位制表示的位元組

大家可以看到,上面輸出的一個個字元編碼,簡單來說,任何非標準的文字檔案(對於Python2來說,標準是ASCLL,對於Python3來說,標準是Unicode),你需要用二進位制讀入這個檔案,然後再用.decode('...')的方法來解碼這個二進位制檔案:

f = open('DegangGuo.txt', 'rb')
# 讀入郭德綱老師的作文, 但是郭老師用的是參合著錯別字的繁體編碼,假設叫個"DeyunCode"
# 那麼你讀入以後,就需要解碼它
u = f.read().decode('DeyunCode')

建議大家還是看看原網頁,因為大部分原理性的東西,我沒有搞過來,只搞了些使用的東西,當然自己有添加了一些東西