python入門之文件處理

一 引入

應用程序運行過程當中產生的數據最早都是存放於內存中的,若想永久保存下來,必需要保存於硬盤中。應用程序若想操做硬件必須經過操做系統,而文件就是操做系統提供給應用程序來操做硬盤的虛擬概念,用戶或應用程序對文件的操做,就是向操做系統發起調用,而後由操做系統完成對硬盤的具體操做。linux

二 文件操做的基本流程

2.1 基本流程

有了文件的概念,咱們無需再去考慮操做硬盤的細節,只須要關注操做文件的流程:windows

# 1. 打開文件,由應用程序向操做系統發起系統調用open(...),操做系統打開該文件,對應一塊硬盤空間,並返回一個文件對象賦值給一個變量f
f=open('a.txt','r',encoding='utf-8') #默認打開模式就爲r
 
# 2. 調用文件對象下的讀/寫方法,會被操做系統轉換爲讀/寫硬盤的操做
data=f.read()
 
# 3. 向操做系統發起關閉文件的請求,回收系統資源
f.close()

2.2 資源回收與with上下文管理

打開一個文件包含兩部分資源:應用程序的變量f和操做系統打開的文件。在操做完畢一個文件時,必須把與該文件的這兩部分資源所有回收,回收方法爲:函數

一、f.close() #回收操做系統打開的文件資源
二、del f #回收應用程序級的變量

其中del f必定要發生在f.close()以後,不然就會致使操做系統打開的文件沒法關閉,白白佔用資源,
而python自動的垃圾回收機制決定了咱們無需考慮del f,這就要求咱們,在操做完畢文件後,必定要記住f.close(),雖然咱們如此強調,可是大多數讀者仍是會不禁自主地忘記f.close(),考慮到這一點,python提供了with關鍵字來幫咱們管理上下文工具

# 一、在執行完子代碼塊後,with 會自動執行f.close()
with open('a.txt', mode='w', coding='utf-8') as f:
    pass
 
# 二、可用用with同時定義讀和寫,用逗號分隔開便可
with open(r'test.txt', mode='r', encoding='utf-8') as rf,\
        open(r'test.txt', mode='r', encoding='utf-8') as rw:
        print(rf.read())  # 打印出所有讀取到的內容

2.3 指定操做文本文件的字符編碼

f = open(...)是由操做系統打開文件,若是打開的是文本文件,會涉及到字符編碼問題,若是沒有爲open指定編碼,那麼打開文本文件的默認編碼很明顯是操做系統說了算了,操做系統會用本身的默認編碼去打開文件,在windows下是gbk,在linux下是utf-8。
這就用到了上節課講的字符編碼的知識:若要保證不亂碼,文件以什麼方式存的,就要以什麼方式打開。
 
f = open('a.txt','r',encoding='utf-8')

三 文件的操做模式

3.1 控制文件讀寫操做的模式

r(默認的):只讀
w:只寫
a:只追加寫

3.1.1 案例一:r 模式的使用

# r只讀模式: 在文件不存在時,則會報錯;文件存在時,文件內指針直接跳到文件開頭
with open('a.txt',mode='r',encoding='utf-8') as f:
     res=f.read() # 會將文件的內容由硬盤所有讀入內存,賦值給res
 
# 小練習:實現用戶認證功能
inp_name=input('請輸入你的名字: ').strip()
inp_pwd=input('請輸入你的密碼: ').strip()
with open(r'db.txt',mode='r',encoding='utf-8') as f:
     for line in f:
         # 把用戶輸入的名字與密碼與讀出內容作比對
         u,p=line.strip('\n').split(':')
         if inp_name == u and inp_pwd == p:
             print('登陸成功')
             break
     else:
         print('帳號名或者密碼錯誤')
 
# db.txt ------> kody:123

3.1.2 案例二:w 模式的使用

# w只寫模式: 在文件不存在時會建立空文檔,文件存在會清空文件,文件指針跑到文件開頭
with open('b.txt',mode='w',encoding='utf-8') as f:
    f.write('你好\n')
    f.write('我好\n')
    f.write('你們好\n')
    f.write('111\n222\n333\n')
#強調:
# 1 在文件不關閉的狀況下,連續的寫入,後寫的內容必定跟在前寫內容的後面
# 2 若是從新以w模式打開文件,則會清空文件內容

3.1.3 案例三:a 模式的使用

# a只追加寫模式: 在文件不存在時會建立空文檔,文件存在會將文件指針直接移動到文件末尾追加新數據
with open('c.txt',mode='a',encoding='utf-8') as f:
     f.write('44444\n')
     f.write('55555\n')
#強調 w 模式與 a 模式的異同:
# 1 相同點:在打開的文件不關閉的狀況下,連續的寫入,新寫的內容總會跟在前寫的內容以後
# 2 不一樣點:以 a 模式從新打開文件,不會清空原文件內容,會將文件指針直接移動到文件末尾,新寫的內容永遠寫在最後
 
# 小練習:實現註冊功能:
name=input('username>>>: ').strip()
pwd=input('password>>>: ').strip()
with open('db1.txt',mode='a',encoding='utf-8') as f:
     info='%s:%s\n' %(name,pwd)
     f.write(info)

3.1.4 案例四:+ 模式的使用(瞭解)

# r+ w+ a+ :可讀可寫
#在平時工做中,咱們只單純使用r/w/a,要麼只讀,要麼只寫,通常不用可讀可寫的模式

3.2 控制文件讀寫內容的模式

# 大前提: tb模式均不能單獨使用,必須與r/w/a之一結合使用
# t(默認的):文本模式
    1. 讀寫文件都是以字符串爲單位的
    2. 只能針對文本文件
    3. 必須指定encoding參數
# b:二進制模式:
   1.讀寫文件都是以bytes/二進制爲單位的
   2. 能夠針對全部文件,針對非文本文件(如圖片、視頻、音頻等)
   3. 必定不能指定encoding參數

3.2.1 案例一:t 模式的使用

# t 模式:若是咱們指定的文件打開模式爲r/w/a,其實默認就是rt/wt/at
with open('a.txt',mode='rt',encoding='utf-8') as f:
     res=f.read()
     print(type(res)) # 輸出結果爲:<class 'str'>
 
with open('a.txt',mode='wt',encoding='utf-8') as f:
     s='abc'
     f.write(s) # 寫入的也必須是字符串類型
 
#強調:t 模式只能用於操做文本文件,不管讀寫,都應該以字符串爲單位,而存取硬盤本質都是二進制的形式,當指定 t 模式時,python解釋器內部幫咱們作了編碼與解碼

3.2.2 案例二: b 模式的使用

# b: 讀寫都是以二進制位單位
with open('1.mp4',mode='rb') as f:
     data=f.read()
     print(type(data)) # 輸出結果爲:<class 'bytes'>
 
with open('a.txt',mode='wb') as f:
     msg="你好"
     res=msg.encode('utf-8') # res爲bytes類型
     f.write(res) # 在b模式下寫入文件的只能是bytes類型
 
#強調:b模式對比t模式
一、在操做純文本文件方面t模式幫咱們省去了編碼與解碼的環節,b模式則須要手動編碼與解碼,因此此時t模式更爲方便
二、針對非文本文件(如圖片、視頻、音頻等)只能使用b模式
 
# 小練習: 編寫拷貝工具
with open(r'冬季.png', 'rb') as rb, \
        open(r'冬季.jpg', 'wb') as wb:
    jpg = rb.read()
    wb.write(jpg)

四 操做文件的方法

4.1 重點

# 讀操做
f.read()  # 讀取全部內容,執行完該操做後,文件指針會移動到文件末尾
f.readline()  # 讀取一行內容,光標移動到第二行首部
f.readlines()  # 讀取每一行內容,存放於列表中
 
# 強調:
# f.read()與f.readlines()都是將內容一次性讀入內容,若是內容過大會致使內存溢出,若還想將內容全讀入內存,則必須分屢次讀入,有兩種實現方式:
# 方式一
with open('a.txt',mode='rt',encoding='utf-8') as f:
    for line in f:
        print(line) # 同一時刻只讀入一行內容到內存中
 
# 方式二
with open('1.mp4',mode='rb') as f:
    while True:
        data=f.read(1024) # 同一時刻只讀入1024個Bytes到內存中
        if len(data) == 0:
            break
        print(data)
 
# 寫操做
f.write  # 寫文件
f.writeable  # 是否可寫
f.writelines()  # for + f.write() 循環寫入多條
 
 
f.write('1111\n222\n')  # 針對文本模式的寫,須要本身寫換行符
f.write('1111\n222\n'.encode('utf-8'))  # 針對b模式的寫,須要本身寫換行符
f.writelines(['333\n','444\n'])  # 文件模式
f.writelines([bytes('333\n',encoding='utf-8'),'444\n'.encode('utf-8')]) #b模式

4.2 瞭解

f.readable()  # 文件是否可讀
f.writable()  # 文件是否可讀
f.closed  # 文件是否關閉
f.encoding  # 若是文件打開模式爲b,則沒有該屬性
f.flush()  # 馬上將文件內容從內存刷到硬盤
f.name

五 主動控制文件內指針移動

#大前提:文件內指針的移動都是Bytes爲單位的,惟一例外的是t模式下的read(n),n以字符爲單位
f.seek(offset,whence)
offset: 相對偏移度 (光標移動的位數)針對的是字節
whence:指定光標位置從何開始
    0:從文件開頭
    1:從當前位置
    2:從文件末尾
 
補充:
    utf-8:
        中文是3個bytes
        英文是1個bytes
    gbk:
        所有都是2個bytes
 
    open函數不設置encoding,默認是gbk
    與encode一毛錢都沒有,encoding只是一個參數
 
    除了read裏面的參數是針對字符,其餘都是針對字節

5.1 案例一: 0模式詳解

# a.txt用utf-8編碼,內容以下(abc各佔1個字節,中文「你好」各佔3個字節)
abc你好
 
# 0模式的使用
with open('a.txt',mode='rt',encoding='utf-8') as f:
    f.seek(3,0)     # 參照文件開頭移動了3個字節
    print(f.tell()) # 查看當前文件指針距離文件開頭的位置,輸出結果爲3
    print(f.read()) # 從第3個字節的位置讀到文件末尾,輸出結果爲:你好
    # 注意:因爲在t模式下,會將讀取的內容自動解碼,因此必須保證讀取的內容是一個完整中文數據,不然解碼失敗
 
with open('a.txt',mode='rb') as f:
    f.seek(6,0)
    print(f.read().decode('utf-8')) #輸出結果爲: 好

5.2 案例二: 1模式詳解

# 1模式的使用
with open('a.txt',mode='rb') as f:
    f.seek(3,1) # 從當前位置日後移動3個字節,而此時的當前位置就是文件開頭
    print(f.tell()) # 輸出結果爲:3
    f.seek(4,1)     # 從當前位置日後移動4個字節,而此時的當前位置爲3
    print(f.tell()) # 輸出結果爲:7

5.3 案例三: 2模式詳解

# a.txt用utf-8編碼,內容以下(abc各佔1個字節,中文「你好」各佔3個字節)
abc你好
 
# 2模式的使用
with open('a.txt',mode='rb') as f:
    f.seek(0,2)     # 參照文件末尾移動0個字節, 即直接跳到文件末尾
    print(f.tell()) # 輸出結果爲:9
    f.seek(-3,2)     # 參照文件末尾往前移動了3個字節
    print(f.read().decode('utf-8')) # 輸出結果爲:好
 
# 小練習:實現動態查看最新一條日誌的效果
import time
with open('access.log',mode='rb') as f:
    f.seek(0,2)
    while True:
        line=f.readline()
        if len(line) == 0:
            # 沒有內容
            time.sleep(0.5)
        else:
            print(line.decode('utf-8'),end='')

5.4 實驗題:獲取文件實時動態變化

寫入文件腳本:編碼

# encoding:utf-8
import time
 
 
res = time.strftime('%Y-%m-%d %H:%M:%S')
print(res)
 
with open('a.txt', 'a', encoding='utf-8') as af:
    af.write(f'獲取時間爲:{res}\n')

獲取文件實時動態:操作系統

# encoding:utf-8
 
 
import time
 
with open(r'a.txt', 'r', encoding='utf-8') as rf:
    while True:
        res = rf.readline()  # 一次讀取一行
        if res:
            print(f'捕捉新增記錄:{res}')

六 文件的修改

# 文件a.txt內容以下
張一蛋     山東    179    49    12344234523
李二蛋     河北    163    57    13913453521
王全蛋     山西    153    62    18651433422
 
# 執行操做
with open('a.txt',mode='r+t',encoding='utf-8') as f:
    f.seek(9)
    f.write('<婦女主任>')
 
# 文件修改後的內容以下
張一蛋<婦女主任> 179    49    12344234523
李二蛋     河北    163    57    13913453521
王全蛋     山西    153    62    18651433422
 
# 強調:
# 一、硬盤空間是沒法修改的,硬盤中數據的更新都是用新內容覆蓋舊內容
# 二、內存中的數據是能夠修改的

文件對應的是硬盤空間,硬盤不能修改對應着文件本質也不能修改,
那咱們看到文件的內容能夠修改,是如何實現的呢?
大體的思路是將硬盤中文件內容讀入內存,而後在內存中修改完畢後再覆蓋回硬盤
具體的實現方式分爲兩種:指針

6.1 文件修改方式一

# 實現思路:將文件內容一次性所有讀入內存,而後在內存中修改完畢後再覆蓋寫回原文件
# 優勢: 在文件修改過程當中同一份數據只有一份
# 缺點: 會過多地佔用內存
with open('a.txt', 'r+', encoding='utf-8') as r_f:
    data = r_f.read()
 
with open('a.txt', 'w+', encoding='utf-8') as w_f:
    w_f.write(data.replace('張一蛋', '張一蛋<婦女主任>'))
 
 
張一蛋<婦女主任>     山東    179    49    12344234523
李二蛋     河北    163    57    13913453521
王全蛋     山西    153    62    18651433422

6.1 文件修改方式二

# 實現思路:以讀的方式打開原文件,以寫的方式打開一個臨時文件,一行行讀取原文件內容,修改完後寫入臨時文件...,刪掉原文件,將臨時文件重命名原文件名
# 優勢: 不會佔用過多的內存
# 缺點: 在文件修改過程當中同一份數據存了兩份
import os
 
with open(r'a.txt', mode='rt', encoding='utf-8') as rf,\
    open(r'b.txt', mode='wt', encoding='utf-8') as wf:
    for line in rf:
        wf.write(line.replace('張一蛋', '張一蛋<婦女主任>'))
 
os.remove('a.txt')
os.rename('b.txt', 'a.txt')
相關文章
相關標籤/搜索