1. 程式人生 > >python3-檔案操作

python3-檔案操作

總結一下python3檔案操作

檔案操作分為讀,寫,修改。

 

讀檔案

示例1:

現有以下檔案:

‘兼職1’   (utf-8編碼格式)

王心    深圳  159    46    13813234424
馬纖羽     深圳  173    50    13744234523
喬亦菲  廣州   172    52    15823423525
羅夢竹     北京    175    49    18623423421
劉諾涵     北京    170    48    18623423765
嶽妮妮     深圳    177    54
18835324553 賀婉萱 深圳 174 52 18933434452 葉梓萱 上海 171 49 18042432324 杜姍姍 北京 167 49 13324523342 black girl 河北 167 50 13542342233

 

f = open(file='兼職1', mode='r', encoding='utf-8')

data = f.read()

print(data)

f.close()


語法操作解釋:
file='兼職1'  表示檔案路徑
mode='r'    表示只讀
encoding='utf-8'  表示將硬碟上的 0101010 按照utf-8的規則去'斷句',再將'斷句'後的每一段0101010轉換成unicode的01010101, unicode對照表中有0101010和字元的對應關係。
f.read()    表示讀取所有內容,內容是已經轉換完畢的字串。
f.close()    表示關閉檔案,檔案操作完之後一定要進行此操作,要不然檔案內容就一直留在記憶體中,浪費記憶體而且佔用著檔案。

##此處encoding必須和檔案在儲存時設定的編碼一致,不然'斷句'會不準確從而造成亂碼。

在windows中,如果encoding省略不指定,則會按系統預設編碼去開啟檔案,也就是說用gbk去開啟utf-8的檔案,則會亂碼。

 

示例2:

 

f = open(file='兼職1',mode='rb')
data = f.read()
f.close()

#打印出來的內容是bytes

上述操作語法解釋:
file = '兼職1'  表示檔案路徑
mode='rb'    表示只讀(可以修改為其他)

f.read()    表示讀取所有內容,內容是硬碟上原來以某種編碼儲存的010101010,即:某種編碼格式的位元組型別  (所以不用encoding)
f.close()    表示關閉檔案

 

示例1和示例2的區別:

示例2開啟檔案時並未指定encoding,是因為直接以rb模式打開了檔案,rb是指二進位制模式,資料讀到記憶體裡直接是bytes格式,如果想要內容,還需要手動decode,因此在檔案開啟階段,不需要指定編碼。

如果我要處理的檔案不知道是什麼編碼的時候,可以用chardet函式來判斷一下。

import chardet

f = open('log',mode='rb')
data = f.read()
f.close()

result = chardet.detect(f.read())
print(result)

輸出:

{'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}

注意:

  • 檔案操作時,以 “r”或“rb” 模式開啟,則只能讀,無法寫入;
  • 硬碟上儲存的檔案都是某種編碼的0101010,開啟時需要注意:
    • rb,直接讀取檔案儲存時原生的0101010,在Python中用位元組型別表示
    • r和encoding,讀取硬碟的0101010,並按照encoding指定的編碼格式進行斷句,再將“斷句”後的每一段0101010轉換成unicode的 010101010101,在Python中用字串型別表示

迴圈檔案

f = open("兼職1",'r',encoding="utf-8")

for line in f:
    print(line)

f.close()

 

寫檔案

 

f = open(file='兼職1',mode='w',encoding='utf-8')
f.write('北大本科美國留學一次50,微訊號:xxxxx')
f.close()

上述語法解釋:
file='兼職1'    表示檔案路徑
mode='r'      表示只寫
encoding='utf-8'  將要寫入的unicode字串編碼成utf-8格式
f.write()      表示寫入內容,寫入的內容是unicode字串型別,內部會根據encoding轉換為指定編碼的01010101,即:位元組型別
f.close()

 

二進位制寫

f = open(file='兼職1',mode='wb')
f.write('北大本科美國留學一次50,微訊號:xxxxx'.encode('utf-8'))
f.close()

上述語法解釋:
file='兼職1'                        表示檔案路徑
mode='wb'                                             表示只以2進位制模式寫
f.write(...)                                          表示寫入內容,寫入的內容必須位元組型別,即:是某種編碼格式的0101010
f.close()

注意:

檔案操作時,以 “w”或“wb” 模式開啟,則只能寫,並且在開啟的同時會先將內容清空。

寫入到硬碟上時,必須是某種編碼的0101010,開啟時需要注意:

  • wb,寫入時需要直接傳入以某種編碼的0100101,即:位元組型別
  • w 和 encoding,寫入時需要傳入unicode字串,內部會根據encoding制定的編碼將unicode字串轉換為該編碼的 010101010

追加

把內容追加到檔案尾部

 

f = open("兼職1",'a',encoding="gbk")

f.write("\n杜姍姍 北京  167 49 13324523342")
f.close()

 

執行結果

姓名        地區    身高    體重    電話
況詠蜜     北京    171    48    13651054608
......
嶽妮妮     深圳    177    54    18835324553
賀婉萱     深圳    174    52    18933434452
葉梓萱    上海    171    49    18042432324
杜姍姍 北京  167 49 13324523342   #這行是新增的

注意:

檔案操作時,以 “a”或“ab” 模式開啟,則只能追加,即:在原來內容的尾部追加內容

寫入到硬碟上時,必須是某種編碼的0101010,開啟時需要注意:

  • ab,寫入時需要直接傳入以某種編碼的0100101,即:位元組型別
  • a 和 encoding,寫入時需要傳入unicode字串,內部會根據encoding制定的編碼將unicode字串轉換為該編碼的 010101010

讀寫模式

開啟模式只有只讀、只寫、只追加,難道沒有可以讀寫的操作嗎?當然有

讀寫模式(先讀後寫)

 

 

f = open("兼職白領學生空姐模特護士聯絡方式.txt",'r+',encoding="gbk")
data = f.read() #可以讀內容 
print(data)
f.write("\nblack girl  河北  167 50  13542342233") #可以寫
f.close()

 

上面寫入的內容是寫到了檔案的最後面,後面還有方式可以寫到任意位置。

寫讀模式

f = open("兼職白領學生空姐模特護士聯絡方式.txt",'w+',encoding="gbk")
data = f.read() 
print(data)

f.write("\nnewline 1哈哈")
f.write("\nnewline 2哈哈")
f.write("\nnewline 3哈哈")
f.write("\nnewline 4哈哈")

print("content",f.read())

f.close()

輸出

         #注意這是個空行, 是上面print(data)的結果,代表 根本 沒讀到內容
content  #從這開始,讀到的是剛寫入的內容
newline 1哈哈
newline 2哈哈
newline 3哈哈
newline 4哈哈

此時檢視檔案 內容 發現,裡面只有4條newline..內容,之前的舊內容全沒了,事實代表,w+會先把檔案清空,再寫新內容,相比w模式,只是支援了一個讀功能,且還只能讀已經寫入的新內容。(沒什麼用)

 

檔案操作的其他功能

 

    def fileno(self, *args, **kwargs): # real signature unknown
        返回檔案控制代碼在核心中的索引值,以後做IO多路複用時可以用到

    def flush(self, *args, **kwargs): # real signature unknown
        把檔案從記憶體buffer裡強制重新整理到硬碟

    def readable(self, *args, **kwargs): # real signature unknown
        判斷是否可讀

    def readline(self, *args, **kwargs): # real signature unknown
        只讀一行,遇到\r or \n為止

    def seek(self, *args, **kwargs): # real signature unknown
        把操作檔案的游標移到指定位置
        *注意seek的長度是按位元組算的, 字元編碼存每個字元所佔的位元組長度不一樣。
        如“路飛學城” 用gbk存是2個位元組一個字,用utf-8就是3個位元組,因此以gbk開啟時,seek(4) 就把游標切換到了“飛”和“學”兩個字中間。
        但如果是utf8,seek(4)會導致,拿到了飛這個字的一部分位元組,列印的話會報錯,因為處理剩下的文字時發現用utf8處理不了了,因為編碼對不上了。少了一個位元組

    def seekable(self, *args, **kwargs): # real signature unknown
        判斷檔案是否可進行seek操作

    def tell(self, *args, **kwargs): # real signature unknown
        返回當前檔案操作游標位置 ,以位元組為單位

    def truncate(self, *args, **kwargs): # real signature unknown
        按指定長度截斷檔案
        *指定長度的話,就從檔案開頭開始截斷指定長度,不指定長度的話,就從當前位置到檔案尾部的內容全去掉。

    def writable(self, *args, **kwargs): # real signature unknown

 

 

 

修改檔案

嘗試直接以r+模式開啟檔案,預設會把新增的內容追加到檔案最後面。但我想要的是修改中間的內容 ,怎麼辦? 為什麼會把內容新增到尾部呢?(最新測試r+會從頭覆蓋,測試程式碼如下)

f1 = open("test.txt",'w',encoding="utf-8")

f1.write("[irving]")

f1.close()

f = open("test.txt",'r+',encoding="utf-8")

f.write("alex")

f.close()

檔案追加操作之所以內容會追加到最後面,是因為,檔案一開啟,要寫的時候,游標會預設移到檔案尾部,再開始寫。 想修改中間部分,seek(中間位置)再寫就可以了。

f = open("兼職1",'r+',encoding="utf-8")

f.seek(6)

f.write("[路飛學城]")

f.close()

輸出

王心[路飛學城]9    46    13813234424
馬纖羽     深圳    173    50    13744234523
喬亦菲     廣州    172    52    15823423525
羅夢竹     北京    175    49    18623423421
劉諾涵     北京    170    48    18623423765
嶽妮妮     深圳    177    54    18835324553
賀婉萱     深圳    174    52    18933434452
葉梓萱    上海    171    49    18042432324
杜姍姍 北京  167 49 13324523342
black girl  河北  167 50  13542342233

結果:從第三個字開始,而且把後面的內容覆蓋了。

這是硬碟的儲存原理導致的,當你把檔案存到硬碟上,就在硬碟上劃了一塊空間,存資料,等你下次開啟這個檔案 ,seek到一個位置,每改一個字,就是把原來的覆蓋掉,如果要插入,是不可能的,因為後面的資料在硬碟上不會整體向後移。所以就出現 當前這個情況 ,你想插入,卻變成了會把舊內容覆蓋掉。

這裡,有兩種辦法解決上述問題。

  1. 佔用硬碟修改
  2. 佔用記憶體修改

佔用硬碟修改:

可以在開啟舊檔案 的同時,生成一個新檔案,邊從舊的裡面一行行的讀,邊往新的一行行寫,遇到需要修改就改了再寫到新檔案 ,這樣,在記憶體裡一直只存一行內容。就不佔記憶體了。 但這樣也有一個缺點,就是雖然不佔記憶體 ,但是佔硬碟,每次修改,都要生成一份新檔案,雖然改完後,可以把舊的覆蓋掉,但在改的過程中,還是有2份資料 的。(os.remove,   os.rename 結合這兩個方法進行覆蓋。)

佔用記憶體修改:

把內容全部讀到記憶體裡,資料在記憶體裡可以隨便增刪改查,修改之後,把內容再全部寫回硬碟,把原來的資料全部覆蓋掉。vim word等各種文字編輯器都是這麼幹的。(可能需要結合seek和truncate來操作)

佔硬碟方式的檔案修改程式碼示例

f_name = "兼職1.txt"
f_new_name = "%s.new" % f_name

old_str = "喬亦菲"
new_str = "[喬亦菲 Yifei Qiao]"

f = open(f_name,'r',encoding="utf-8")
f_new = open(f_new_name,'w',encoding="utf-8")

for line in f:

    if old_str in line:
        new_line = line.replace(old_str,new_str)
    else:
        new_line = line

    f_new.write(new_line)

f.close()
f_new.close()

上面的程式碼,會生成一個修改後的新檔案 ,原檔案不動,若想覆蓋原檔案

import os

f_name = "兼職白領學生空姐模特護士聯絡方式utf8.txt"
f_new_name = "%s.new" % f_name

old_str = "喬亦菲"
new_str = "[喬亦菲 Yifei Qiao]"

f = open(f_name,'r',encoding="utf-8")
f_new = open(f_new_name,'w',encoding="utf-8")

for line in f:

    if old_str in line:
        new_line = line.replace(old_str,new_str)
    else:
        new_line = line

    f_new.write(new_line)

f.close()
f_new.close()

os.rename(f_new_name,f_name) #把新檔名字改成原檔案 的名字,就把之前的覆蓋掉了,windows使用os.replace # 幫助文件說明replace會覆蓋原檔案
#windows rename不會覆蓋,如果用rename前必須先move掉原檔案