1. 程式人生 > >第8天檔案操作

第8天檔案操作

1. 檔案操作

檔案就是計算機的作業系統虛擬給使用者操作硬碟的一個介面。我們都知道,記憶體和硬碟都是儲存資料的,但是記憶體的資料易丟失,當我們需要在下次啟動計算機的時候還能夠看到我們寫的東西,就必須要把我們的東西寫入到硬碟中,這也就是檔案存在的意義。

操作檔案的三個步驟
1. 開啟檔案得到一個檔案控制代碼2. 通過這個檔案控制代碼來對檔案進行我們想要的操作3. 向作業系統傳送關閉指令,回收作業系統資源

2. 開啟檔案的模式

開啟檔案的模式總共有五種,三種純淨模式,r, w,    a,  以及控制開啟檔案格式的兩種模式: t, b 

'''
控制檔案格式的兩種模式必須和三種純淨模式一塊進行使用,不能單獨使用:
三種純淨模式:
    r: 只讀,檔案指標在預設在開頭,檔案不存在就跑出異常
    w: 只寫,檔案指標也在開頭,檔案不存在就建立,存在就清空內容寫入
    a: 追加寫入,檔案指標在結尾,檔案不存在就建立,存在就追加寫入
控制檔案格式的兩種模式:
    t:文字格式:只能操作文字檔案,
在這樣的格式下一般需要指定處理文字的編碼方式,否則無法跨平臺進行使用
   b: 二進位制格式: 可以操作所有的檔案,因此儲存就是用的位元組,所以在此模式下不能指定編碼方式 '''

(1). r只讀模式:在檔案不存在的時候報錯,檔案存在指標直接跳到開頭

簡單的開啟檔案的方式:但是不建議這樣寫,因為如果沒有指定mode和編碼方式,採取的都是預設的,mode為rt,編碼方式為作業系統預設的編碼,如果一旦出現了跨平臺的操作,就會出現亂碼的問題!

with open('user_gbk') as f:
    print(f.readline(11))# 無論開啟什麼檔案,都建議指定模式和編碼方式with open('user_gbk', mode='rt', encoding='gbk') as f:  print(f.read())

檔案讀取的兩種模式之 【rt】模式

# 以rt模式開啟的建議指定上字元編碼,單位為字元  with open('user_utf-8', 'rt', encoding='utf-8') as f:    # 把一個檔案全部的內容讀取出來,返回一個字串,一般不建議這樣讀檔案,容易撐爆記憶體
    # data = f.read()
    # print(data, type(data), len(data))
    # 讀取當前指標所在行的值,傳遞的引數代表的是讀取幾個字元
    data = f.readline(4)
    print(data)
    # 把每一行的值轉換成一個字串,最後返回一個列表
data = f.readlines() print(data)

檔案讀取的兩種模式之 【rb】模式

# 以rb模式開啟的不能指定字元編碼,單位都是位元組
with open('user_utf-8', 'rb') as f:
#     for line in f:
#         print(len(line))
    # 把一個檔案全部的內容讀取出來,返回一個bytes型別,一般不建議這樣讀檔案,容易撐爆記憶體
    # 如果我們想檢視內容,就用寫入檔案編碼方式解碼
    # data = f.read()
    # print(data, type(data), len(data), data.decode('utf-8'))
    # 讀取當前指標所在行的值,傳遞的引數代表的是讀取幾個位元組
    data = f.readline(3)
    print(data)
    # 把每一行的值轉換成bytes型別,最後返回一個列表
    data = f.readlines()
    print(data)

例題:以rb的形式讀取一張圖片

with open('ceshi.png', 'rb') as f:
    data = f.read()
    print(data)

 (2)r只寫模式:在檔案不存在的時候建立,檔案存在指標直接跳到開頭,覆蓋寫入檔案

檔案寫入的兩種模式之 【wt】模式

# wt的模式寫入的必須是字串
with open('user_utf-8', 'wt', encoding='utf-8') as f:
    f.write('中國你好!'.encode('utf-8'))
    line = ['111', '222', '444']
    # 下面這兩種方法是一樣的
    for i in line:
        f.write(i)
    f.writelines(line)

 檔案寫入的兩種模式之 【wb】模式

# 寫入的是位元組,因此當我們在寫入的時候建立了
# 一個字串需要先進行編碼才能夠進行寫入
with open('user_utf-8', 'wb') as f:
    f.write('中國你好好!'.encode('utf-8'))

(3).a只寫模式:在檔案不存在的時候建立,檔案存在指標直接跳到檔案結尾,追加寫入檔案,和w的區別就是一個是清空寫入,一個是追加寫入,其他的都是一樣的。

例題:做一個簡單的拷貝檔案的工具

# 拷貝檔案的小工具
src_file = input('原始檔路徑>>').strip()
dst_file = input('目的檔案路徑>>').strip()
with open(src_file, 'rb') as read_f, open(dst_file, 'wb') as write_f:
    for line in read_f:
        write_f.write(line)

3. 檔案內游標的移動

游標的移動在【rt】模式下是是字元為單位的,但是在【rb】的形式下是以位元組為單位的

# rt模式 讀的是字元
with open('user_utf-8', 'rt', encoding='utf-8') as f:
    data = f.read(3)
    print(data)

# rb模式,讀的是位元組,如果想顯示出原始的字元,得先進行解碼
with open('user_gbk', 'rb') as f:
    # data = f.read(3)
    # b'\xe4\xb8\xad'
    data = f.read(2)
    print(data, data.decode('gbk'))

控制檔案指標移動的方法:

f.seek(引數一, 引數二)

引數一: 指的是移動的位元組數
引數二:0 模式是預設模式,在任何的開啟方式下都可以使用,但是1,和2 只能在b模式下才可以進行使用
    0:檔案指標以檔案開頭為參照移動【引數一】個位元組數,移動的位元組數不能為負
    1: 檔案指標以當前指標所在位置為參照移動【引數一】個位元組數, 負數為左,正數為右
    2: 檔案指標以檔案末尾為參照移動【引數一】個位元組數f.tell()  檢視當前指標離檔案頭部的位元組數

0模式:

# f.seek的0模式,f.seek的指標都是以位元組形式移動的
# 在我們人為操作移動的過程中,不能把一個字元給拆分開
# 否則在列印的時候會報錯
# rt模式
with open('user_utf-8', 'rt', encoding='utf-8') as f:
    f.read()
    f.seek(3, 0)
    print(f.tell())
    print(f.read())

# rb模式
with open('user_utf-8', 'rb') as f:
    f.seek(3, 0)
    print(f.tell())
    print(f.read().decode('utf-8'))

1 模式

# f.seek的1模式,
# 1.只能在b模式下進行使用
# 2.偏移量可負可正,負為左,正為右
# 3.指標移動不能拆分字元,否則在解碼的時候會報錯
with open('user_utf-8', 'rb') as f:
    f.read()
    f.seek(-3, 1)
    print(f.read().decode('utf-8'))

2 模式

# f.seek的2模式
# 1. 只能在b模式下使用
# 2. 偏移量可正可負,正返回為空,負代表指標移動
# 3.指標移動不能拆分字元,否則在解碼的時候會報錯
with open('user_utf-8', 'rb') as f:
    f.seek(-3, 2)
    print(f.read().decode('utf-8'))

例題:tail -f access.log  模擬當前命令

command = input('請輸入命令(Q退出)>>').strip()
while True:
    try:
        if command.upper() == 'Q':
            break
        if command.split()[0] == 'tail' and command.split()[1] == '-f':
            # 輸入的命令沒有錯誤,獲取檔名
            file_path = command.split()[2]
            print(file_path)
            with open(file_path, 'rb') as f:
                # 移動指標到尾部
                f.seek(0, 2)
                while True:
                    line = f.readline()
                    # 如果沒有檔案匯入,就繼續迴圈
                    if len(line) == 0:
                        continue
                    else:
                        # 如果有檔案匯入就寫入檔案
                        print(line.decode('utf-8'))
        else:
            print('usage: tail -f file_path!')
    except:
        print('usage: tail -f file_path!')

4. 檔案的修改

   檔案就是計算機的作業系統給我們對映的一塊硬碟空間。對於硬碟而言,是沒有辦法修改的,我們對於硬碟的操作也只能是覆蓋寫入,因此檔案也是不能修改的。但是我們會發現,我們每次開啟檔案的是可以對其進行的修改的,這究竟是怎麼回事呢?是因為記憶體。我們的資料在硬碟上是沒有辦法修改,但是在記憶體中確是可以進行修改的,因此我們通常所說的檔案修改其實指的就是記憶體的修改。通過記憶體修改完成之後重新寫入硬碟中的。

檔案修改的兩種方式:

1. 把檔案全部載入到記憶體中,然後在記憶體中修改我們的資料,之後統一的寫入檔案中  優點:在檔案修改過程中同一份資料只有一份  缺點:會佔用過多的記憶體
2. 把檔案一行一行的載入到記憶體中,一行行的修改之後寫入到一個新的檔案,當修改完成之後,刪除原始檔,然後把新的檔名字改成原始檔的名字。  優點:不會佔用過多的記憶體  缺點:在檔案修改過程中,會有兩份資料存在硬碟中。

驗證硬盤裡的資料是不能修改的:

檔案內容:

你好,啊slegeg
我好
大家好
# 檔案內容:
'''
你好,啊slegega
我好
大家好
'''

with open('user_utf-8', 'r+t', encoding='utf-8') as f:
    # 移動指標到第一行的【啊】後面
    f.seek(12, 0)
    # 寫入【中國好】三個字
    # 如果硬碟資料能夠進行修改第一行一概出現:你好,啊中國好slegega
    # 如果硬碟不能改只能覆蓋,應該出現:你好,啊中國好我好(因為把這一行後面的換行符也去掉了)
    f.write('中國好')

檔案的修改方法:

檔案內容:

kevin is dsb
dsb is kevin
alex is kevin 

第一種方式的把kevin全部改成SB

# 內容全部載入到記憶體
with open('a.txt', 'rt', encoding='utf-8') as f:
    res = f.read().replace('kevin', 'SB')

# 將修改的資料重新寫入檔案中
with open('a.txt', 'wt', encoding='utf-8') as f:
    f.write(res)

第二種方式把所有‘SB'全部改成kevin

# 首先開啟以rt得方式開啟原始檔,然後以wt的形式開啟一個新檔案
with open('a.txt', 'rt', encoding='utf-8') as read_file,\
        open('.a.txt.swap', 'wt', encoding='utf-8') as write_file:
    # 修改一個值並寫入到一個新的檔案中
    for line in read_file:
        res = line.replace('SB', 'kevin')
        write_file.write(res)
import os
os.remove('a.txt')
os.rename('.a.txt.swap', 'a.txt')