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')