1. 程式人生 > 實用技巧 >Python-檔案處理

Python-檔案處理

一、檔案處理

什麼是檔案

檔案是作業系統提供給使用者/應用程式操作硬碟的一個虛擬單位

為何要使用檔案

存取硬碟必須使用檔案

如何用檔案

開啟檔案的方法:open(檔案路徑,檔案開啟模式,編碼方式等)
當使用open(檔案路徑,開啟模式)時候,由應用程式幫助我們向作業系統發出申請,要開啟一個檔案,open(檔案路徑,開啟模式)會產生一個返回值,叫檔案物件(也叫檔案控制代碼)。如下面的f
f = open(檔案路徑,開啟模式),此時會佔用作業系統的資源(因為要求作業系統開啟檔案),也會佔用應用程式的資源(因為產生了檔案物件f,需要記憶體空間)。
當檔案操作結束之後,因為python有垃圾回收機制,就如同我們定義的x = 1一樣,會自己被回收。而作業系統不會自己關閉檔案,所以我們需要幫助作業系統關係,
語法是f.close()
使用檔案的基本三個步驟:
1.f = open(檔案位置,開啟模式)
2.f.write(資料)
3.f.close()
應用程式/使用者=》檔案物件/檔案控制代碼

=》遙控
作業系統===========》檔案 =================》 空調
硬體 ==============》系統

絕對路徑

從系統盤開始到目標檔案的路徑

如Windows系統下:D:\a\b\c.txt
     file_path = r'D:\a\b\c.txt'
linux系統共下:a/b/c.txt

相對路徑

以當前目錄為首發位置,利用一個.來代表當前路徑

r模式

r模式只適用文字

f = open(r'a.txt',mode='rt',encoding='utf-8')
res = f.read()
print(res)
f.close()

rb模式

rb模式可以用以操作圖片等,因為b模式讀出來的是硬碟的原生內容即二進位制,因為pycharm的優化,我們看到的是轉化為16進位制的數字。

f = open(r'a.txt',mode='rb')
res = f.read()
s = res.decode('utf-8')
print(s)
f.close()

二、檔案的開啟模式

檔案的開啟模式分類兩大類:

控制檔案讀寫操作的模式

'''1.1 r:只讀(預設的):在檔案不存在時則報錯,檔案存在時檔案指標跳到檔案開頭'''

f = open('a.txt',mode='rt',encoding='utf-8')
res = f.read()
print(f.readable())  # 判斷檔案當前是否可讀
print(f.writable())  # 判斷檔案當前是否可寫
f.close()
------------------------------------------------
True
False

'''1.2 w:只寫:在檔案不存在時則建立空檔案,檔案存在時則清空,檔案指標跳到檔案開頭'''

f.txt=open('b.txt',mode='wt',encoding='utf-8')
f.txt.write("落霞與孤鶩齊飛\n")
f.txt.write("秋水共長天一色\n")
f.txt.close()

'''1.3 a:只追加寫:在檔案不存在時則建立空檔案,檔案存在時也不會清空,檔案指標跳到檔案末尾'''

f.txt=open('c.txt',mode='at',encoding='utf-8')
f.txt.write("大弦嘈嘈如急雨\n")
f.txt.write("小弦切切如私語\n")
f.txt.close()
'''
總結:w與a的異同
相同點:在打開了檔案不關閉的情況下,連續地寫入,新的內容永遠跟在老內容之後
不同點:重新開啟檔案,w會清空老的內容,而a模式會保留老的內容並且指標跳到檔案末尾
'''

# 示範1:註冊功能
name = input("your name: ").strip()
'''
做合法性校驗:
1、如果輸入的使用者名稱包含特殊字元^$&...讓使用者重新輸入
2、如果輸入的使用者名稱已經存在也重新輸入
'''
pwd = input("your password: ").strip()
'''
做合法性校驗:
1、密碼長度
2、如果密碼包含特殊字元則重新輸入
'''
f.txt = open('user.txt',mode='at',encoding='utf-8')
f.txt.write('%s:%s\n' %(name,pwd))
f.txt.close()

# 示範2:登入功能
inp_name = input("your name: ").strip()
inp_pwd = input("your pwd: ").strip()

f.txt = open('user.txt',mode='rt',encoding='utf-8')
for line in f.txt:
    user,pwd=line.strip('\n').split(':')
    if inp_name == user and inp_pwd == pwd:
        print('login successful')
        break
else:
    print('user or password error')

f.txt.close()

控制檔案讀寫內容的模式

encode('utf-8'):以utf-8的格式編碼
decode('utf-8'):以utf-8的格式解碼

'''1.1 t(預設):讀寫都是以str字串為單位,一定要指定encoding'''

f=open('a.txt',mode='rt',encoding='utf-8')
print(f.read())
f.close()
----------------------------------------------
yang

'''1.2 b:讀寫都是以bytes為單位,一定不能指定encoding引數'''
"""讀取時候"""
f=open('a.txt',mode='rb')
data=f.read()
print(data,type(data))
print(data.decode('utf-8'))
f.close()
-----------------------------------------
b'yang\r\negon\r\n' <class 'bytes'>
yang

'''寫入時候'''
f.txt=open('d.txt',mode='wb')
f.txt.write("egon".encode('utf-8'))
f.txt.close()

# ```示範1:檔案拷貝程式```
src_file = input("請輸入原始檔路徑:").strip()
dst_file = input('請輸入目標檔案路徑:').strip()
with open(src_file,mode='rb') as src_f,\
    open(dst_file,mode='rb') as dst_f:
    for line in src_f:
        dst_f.write(line)
'''
為防止佔用過多記憶體空間,應該一邊讀一邊寫
若所有的檔案一下子都讀到記憶體,會將記憶體撐爆,系統會卡頓
因為同一時間,也只能由記憶體往硬碟內寫一行(以換行符為一行,for迴圈也一樣),多了也寫不了。所以一堆一下子都取出來也沒有意義
'''

# 上下文管理with
with open() as f1,open() as f2:
    f.txt.read()


預設的是rt
with open('a.txt') as f:
    pass

with open('a.txt',mode='w+') as f:
    pass

rwa為三種操作模式
tb這兩種讀取內容的模式必須和rwa聯用,如wb,rb等
+必須與rwa聯用:r+、w+、a+
如下:r+t,w+t,a+t,r+b,w+b,a+b

三、檔案操作的其他方法

'''f.read(),f.readline(),f.readlines()的使用'''
with open('a.txt',mode='r',encoding='utf-8') as f:
    data1 = f.read()  # 一次性取完檔案,字串形式存入data1
    f.seek(0,0)
    data2 = f.readline()  # 一次只讀一行內容,字串形式存入data2
    f.seek(0, 0)
    data3 = f.readlines()  # 一次性取完檔案,以\n為結尾,按照列表形式存入data3
    print(data1,type(data1))
    print(data2,type(data2))
    print(data3,type(data3))
----------------------------------------------------------
111
222
333
 <class 'str'>
111
 <class 'str'>
['111\n', '222\n', '333\n'] <class 'list'>

f.read(),f.readline(),f.readlines()與for迴圈的搭配使用

# f.read()與for迴圈搭配,每次取出一個字元('\n'算一個字元)
with open('a.txt',mode='r',encoding='utf-8') as f:
    for i in f.read():
        print(i,type(i))
-------------------------------------------------------
1 <class 'str'>
1 <class 'str'>
1 <class 'str'>

 <class 'str'>
2 <class 'str'>
2 <class 'str'>
2 <class 'str'>

 <class 'str'>
3 <class 'str'>
3 <class 'str'>
3 <class 'str'>

 <class 'str'>

Process finished with exit code 0

# f.readline()與for迴圈搭配,每次取出一個字元('\n'算一個字元)
with open('a.txt',mode='r',encoding='utf-8') as f:
    for i in f.readline():
        print(i,type(i))
--------------------------------------------------
1 <class 'str'>
1 <class 'str'>
1 <class 'str'>

 <class 'str'>
# f.readlines()與for迴圈搭配,每次取出列表中的一個元素,即對應檔案中的一行字串
with open('a.txt',mode='r',encoding='utf-8') as f:
    for i in f.readlines():
        print(i,type(i))
--------------------------------------------------------------------------
111
 <class 'str'>
222
 <class 'str'>
333
 <class 'str'>

讀相關方法

with open('a.txt',mode='rt',encoding='utf-8') as f.txt:
'''一行一行讀'''
    line1=f.readline()
    line2=f.readline()
    line3=f.readline()
    print(line1)
    print(line2)
    print(line3)
# 1.迴圈讀出
    lines=[]
    for line in f:
        lines.append(line)
    print(lines)
# 2.  簡化
    lines=f.readlines()
    print(lines)
'''方法2其實就是方法1的縮寫,將檔案內所有內容取出來,按照\n分割,存入列表'''

寫相關方法

with open('a.txt',mode='wt',encoding='utf-8') as f.txt:
# 1.用迴圈寫入
    lines=['111\n','222\n','333\n']
    for line in lines:
        f.write(line)
# 2.簡化
    lines = ['111\n', '222\n', '333\n']
    f.writelines(lines)
# 3.直接將hello寫入
    f.writelines("hello")


其他:
with open(r'.\a.txt',mode='wt',encoding='utf-8') as f:
    # print(f.txt.name) # 取的是開啟檔案的路徑
    # print(f.txt.closed)
    for i in range(100):
        f.write("%s\n" %i)
        f.flush()

四、控制檔案內指標移動

控制檔案內指標的移動都是以位元組為單位
只有一種特殊情況,t模式下的read(n),代表的是n個字元,此外代表的全都是位元組

此時f.txt內容如下:

你好
2222
3333
4444
with open('f.txt',mode='rt',encoding='utf-8') as f:
    data=f.read(6) # 6個字元
    print(data)  # 列印了你好和一個換行符和一個2,說明是以字元為一個單位
------------------
你好
222
    

with open('f.txt',mode='rb') as f:
    # data=f.read(6) # 6個位元組
    data=f.read(8) # 8個位元組
    print(data.decode('utf-8'))  # utf-8中你好佔了六個位元組,一個換行符加2,一共八個字元
-----------------------
你好
222


f.txt.seek(n,模式) # n代表的移動的位元組個數

'''0模式:參照檔案的開頭開始移動(只有0模式可以在t下使用,1和2模式只能在b下使用)'''
with open('f.txt',mode='rt',encoding='utf-8') as f:
    f.seek(5,0)
    print(f.tell())
    print(f.read())
    print(f.tell())
    print('='*100)
    f.seek(0,0)
    print(f.read())

'''1模式:參照指標當前所在的位置'''
with open('f.txt',mode='rb') as f:
    f.seek(3,1)
    f.seek(3,1)
    print(f.tell())
    # f.seek(2,1)
    f.seek(5,0)
    print(f.read().decode('utf-8'))

'''2模式:參照檔案末尾的位置'''
with open('f.txt',mode='rb') as f:
    f.seek(0,2)
    f.seek(-3,2)
    # print(f.tell())
    print(f.read().decode('utf-8'))


'''模擬程式記錄日誌的功能
time.strftime("%Y年%m月%d日 %H:%M:%S"),可將時間格式化
'''
import time
for i in range(10000):
    with open('access.log.txt',mode='at',encoding='utf-8') as f:
        s = time.strftime("%Y年%m月%d日 %H:%M:%S")
        content = "收入%s萬\n" %i
        res = "{} {}".format(s,content)
        f.write(res)
        time.sleep(1)
'''
模擬程式讀取日誌
'''
import time
with open('access.log.txt', mode="rb") as f:
    f.seek(0,2)
    while True:
        line = f.read()
        if len(line) == 0:
            time.sleep(1)
        else:
            print(line.decode('utf-8'))
'''
f.truncate(n)從檔案開頭往後數n個位元組保留下來,其餘全部刪除
f.truncate()從檔案開頭往後數指標當前所在的位置,其餘全部刪除
'''
with open('f.txt',mode='ab') as f:
    f.truncate(3)
    f.seek(-3,2)
    f.truncate()

五、檔案修改的兩種方法

with open('a.txt',mode='r+t',encoding='utf-8') as f:
    print(f.writable())
    f.seek(7,0)
    f.write('SB')


with open('a.txt',mode='r+t',encoding='utf-8') as f:
    f.seek(3,0)
    f.write('h')

由上例得出結論:硬碟都是用新內容覆蓋舊內容,沒有修改的概念,但是記憶體是可以修改的.

如何修改檔案

思路:把硬碟的內容先讀入記憶體,然後在記憶體中修改完畢後,再覆蓋到硬碟

'''
方式一:如我們常使用的word,notpad++都是此方法
步驟:
1、先將硬碟中檔案的內容全部讀入記憶體,然後在記憶體中修改完畢得到一個修改好的結果
2、將修改的結果覆蓋回原檔案
優點: 不耗費硬碟
缺點:耗費記憶體
'''
with open('a.txt',mode='rt',encoding='utf-8') as f1:
    data=f1.read()
    res=data.replace('lxx','SB')

with open('a.txt',mode='wt',encoding='utf-8') as f2:
    f2.write(res)

'''
方式二:
步驟:
1、迴圈讀取原始檔內容,一行行修改一行行寫入一個新的臨時檔案
2、刪除原始檔
3、將臨時檔案重新命名為原始檔名
優點:節省記憶體
缺點:耗費硬碟空間
'''
import os
with open('a.txt',mode='rt',encoding='utf-8') as f1,\
        open('.a.txt.swp',mode='wt',encoding='utf-8') as f2:
    for line in f1:
        res=line.replace('SB','lxx')
        f2.write(res)

os.remove('a.txt')
os.rename('.a.txt.swp','a.txt')