1. Python編碼解碼
將文本轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)就是編碼,將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換為文本就是解碼。編碼和解碼要按照一定的規(guī)則進(jìn)行,這個(gè)規(guī)則就是字符集。
# -*- coding: utf-8 -*-
# 本文件應(yīng)該保存為utf-8編碼,否則會(huì)報(bào)錯(cuò)
str = "中文測(cè)試"
print(f'Unicode字符串為"{str}"')
byte0 = str.encode("utf-8")
print(f'Unicode字符串"{str}"以u(píng)tf-8編碼得到字節(jié)串[{byte0}]')
str0 = byte0.decode("utf-8")
print(f'將utf-8字節(jié)串[{byte0}]解碼得到字符串"{str0}"')
byte1 = str.encode("gbk")
print(f'Unicode字符串"{str}"以gbk編碼得到字節(jié)串[{byte1}]')
str1 = byte1.decode("gbk")
print(f'將gbk字節(jié)串[{byte1}]解碼得到Unicode字符串"{str1}"')
print(f'以文本方式將Unicode字符串"{str}"寫(xiě)入a.txt')
with open("a.txt", "w", encoding="gbk") as f:
f.write(str)
print("以文本方式讀取a.txt的內(nèi)容")
with open("a.txt", "r", encoding="gbk") as f:
print(f.read())

默認(rèn)編碼可以通過(guò)sys.getdefaultencoding()來(lái)查看Python解釋器會(huì)用的默認(rèn)編碼:
import sys
encoding = sys.getdefaultencoding()
print(f"{encoding}")
2. 文件操作
2.1 普通文件操作
f = open("a.txt") #打開(kāi)文件,得到一個(gè)文件句柄,并賦值給一個(gè)變量
print(f.read()) #打印讀取文件的內(nèi)容
f.close() #關(guān)閉文件
open函數(shù)原型如下:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
其中:
(1)參數(shù)file是一個(gè)表示文件名稱(chēng)的字符串,如果文件不在程序當(dāng)前的路徑下,就需要在前面加上相對(duì)路徑或絕對(duì)路徑。
(2)參數(shù)mode是一個(gè)可選字參數(shù),指示打開(kāi)文件的方式,若不指定,則默認(rèn)為以讀文本的方式打開(kāi)文件。

默認(rèn)的打開(kāi)方式是'rt'(mode='rt')。Python是區(qū)分二進(jìn)制方式和文本方式的,當(dāng)以二進(jìn)制方式打開(kāi)一個(gè)文件時(shí)(mode參數(shù)后面跟'b'),返回一個(gè)未經(jīng)解碼的字節(jié)對(duì)象:當(dāng)以文本方式打開(kāi)文件時(shí)(默認(rèn)是以文本方式打開(kāi),也可以mode參數(shù)后面跟't'),返回一個(gè)按系統(tǒng)默認(rèn)編碼或參數(shù)encoding傳入的編碼來(lái)解碼的字符串對(duì)象。
(3)buffering是一個(gè)可選的參數(shù),buffering=0表示關(guān)閉緩沖區(qū)(僅在二進(jìn)制方式打開(kāi)時(shí)可用):buffering=1表示選擇行緩沖區(qū)(僅在文本方式打開(kāi)時(shí)可用):buffering大于1時(shí),其值代表固定大小的塊緩沖區(qū)的大小。當(dāng)不指定該參數(shù)時(shí),默認(rèn)的緩沖策略是這樣的:二進(jìn)制文件使用固定大小的塊緩沖區(qū),文本文件使用行緩沖區(qū)。
# -*- coding: utf-8 -*-
f = open("wb.txt", "w", encoding="utf-8")
f.write("測(cè)試w方式寫(xiě)入,如果文件存在,則清空內(nèi)容后寫(xiě)入:如果文件不存在,則創(chuàng)建\n")
f.close()
f = open("wb.txt", "a", encoding="utf-8")
f.write("測(cè)試a方式寫(xiě)入,如果文件存在,則在文件內(nèi)容后最后追加寫(xiě)入;如果文件不存在,則創(chuàng)建")
f.close()
f = open("wb.txt", "r", encoding="utf-8")
# 以文本方式讀,f.read()返回字符串對(duì)象
data = f.read()
print(type(data))
print(data)
f.close()
f = open("wb.txt", "rb")
# 以文本方式讀,f.read()
data = f.read()
print(type(data))
print(data)
print('將讀取的字符對(duì)象解碼:')
print(data.decode('utf-8'))
f.close()

從上面的例子可以看出,以二進(jìn)制讀取文件時(shí),讀取的是文件字符串的編碼(以encoding指定的編碼格式進(jìn)行的編碼),將讀取的字節(jié)對(duì)象解碼,可以得出原字符串。
需要注意以下幾點(diǎn):
(1)記得使用完畢后及時(shí)關(guān)閉文件,釋放資源,如f.close(),否則就會(huì)導(dǎo)致操作系統(tǒng)打開(kāi)的文件還沒(méi)有關(guān)閉,白白占用資源。使用with關(guān)鍵字來(lái)幫我們管理上下文,系統(tǒng)會(huì)自動(dòng)為我們關(guān)閉文件和處理異常,如下面兩行代碼即可完成安全的寫(xiě)操作。
with open('a.txt', 'w') as f:
f.write("hello word")
(2)open()函數(shù)是由操作系統(tǒng)打開(kāi)文件,如果我們沒(méi)有為open指定編碼,那么打開(kāi)文件的默認(rèn)編碼很明顯是操作系統(tǒng)默認(rèn)的編碼:在Windows下是gbk,在Linux下是utf-8。若要保證不亂碼,就必須讓讀取文件和寫(xiě)入文件使用的編碼一致。
常見(jiàn)的文件操作方法:
f.readable():判斷有讀取文件的方法
f.read([size]):size為讀取的長(zhǎng)度,以byte為單位,如果未給定或?yàn)樨?fù)則讀取所有
f.readline([size]) :讀取整行,包括 "\n" 字符
f.readlines([size]) : 返回一個(gè)list,其實(shí)內(nèi)部是調(diào)用readline()實(shí)現(xiàn)的
f.write(str) : 把str寫(xiě)到文件中,write()不主動(dòng)加換行符
f.writelines(str) : 把多行內(nèi)容一次寫(xiě)入
f.close() : 關(guān)閉文件
f.flush() : 把緩沖區(qū)的內(nèi)容寫(xiě)入硬盤(pán)
f.tell() : 當(dāng)前文件操作標(biāo)記的位置
f.next() : 返回下一行,并將文件操作標(biāo)記位移到下一行。把一個(gè)file用于for … in file這樣的語(yǔ)句時(shí),就是調(diào)用next()函數(shù)來(lái)實(shí)現(xiàn)遍歷的
f.seek(offset[,whence]) :將文件打操作標(biāo)記移到offset的位置。這個(gè)offset一般是相對(duì)于文件的開(kāi)頭來(lái)計(jì)算的,一般為正數(shù)。whence參數(shù),whence可以為0表示從頭開(kāi)始計(jì)算,1表示以當(dāng)前位置為原點(diǎn)計(jì)算。2表示以文件末尾為原點(diǎn)進(jìn)行計(jì)算。需要注意,如果文件以a或a+的模式打開(kāi),每次進(jìn)行寫(xiě)操作時(shí),文件操作標(biāo)記會(huì)自動(dòng)返回到文件末尾
在文件中定位示例:
f = open("tmp.txt", "rb+")
a = f.write(b"abcdefghi")
print(f"一共有{a}個(gè)字節(jié)")
b = f.seek(5) #移動(dòng)到文件的第六個(gè)字節(jié)
print(f"第六個(gè)字節(jié):")
print(f.read(1))
c = f.seek(-3,2) #移動(dòng)到文件的倒數(shù)第三個(gè)字節(jié)
print(f"倒數(shù)第三個(gè)字節(jié):{c}")
print(f.read(1))

基于seek實(shí)現(xiàn)類(lèi)似Linux命令tail -f的功能:
# encoding=utf-8
import time
with open('tmp.txt', 'rb') as f:
f.seek(0, 2) # 將光標(biāo)移動(dòng)到文件末尾
while True: # 實(shí)時(shí)顯示文件新增加的內(nèi)容
line = f.read()
if line:
print(line.decode('utf-8'), end='')
else:
time.sleep(0.2) # 讀取完畢后短暫的睡眠
當(dāng)tmp.txt追加新的內(nèi)容時(shí),新內(nèi)容會(huì)被程序立刻打印出來(lái)。
2.2 大文件的讀取
當(dāng)文件較小時(shí),我們可以一次性全部讀入內(nèi)存,對(duì)文件的內(nèi)容作出任意修改,再保存至磁盤(pán),這一過(guò)程會(huì)非???。
將文件a.txt中的字符串str1替換為str2:
with open('a.txt') as read_f,open('.a.txt.swap', 'w') as write_f:
data=read_f.read() #全部讀入內(nèi)存,如果文件很大,則會(huì)很卡
data=data.replace('str1', 'str2') #在內(nèi)存中完成修改
write_f.write(data) #一次性寫(xiě)入新文件
os.remove('a.txt')
os.rename('.a.txt.swap', 'a.txt')
當(dāng)文件很大時(shí),如GB級(jí)的文本文件,上面的代碼運(yùn)行將會(huì)非常緩慢,此時(shí)我們需要使用文件的可迭代方式將文件的內(nèi)容逐行讀入內(nèi)存,再逐行寫(xiě)入新文件,最后用新文件覆蓋源文件。
對(duì)大文件進(jìn)行讀寫(xiě):
import os
with open('a.txt') as read_f,open('.a.txt.swap','w') as write_f:
for line in read_f: #對(duì)可迭代對(duì)象f逐行操作,防止內(nèi)存溢出
line=line.replace('str1', 'str2')
write_f.write(line)
os.remove('a.txt')
os.rename('.a.txt.swap', 'a.txt')
大文件為a.txt,當(dāng)打開(kāi)文件時(shí),會(huì)得到一個(gè)可迭代對(duì)象read_f,對(duì)可迭代對(duì)象進(jìn)行逐行讀取,可防止內(nèi)存溢出,也會(huì)加快處理速度。
處理大數(shù)據(jù)還有多種方法:
(1)通過(guò)read(size)增加參數(shù),指定讀取的字節(jié)數(shù)。
while True:
block = f.read(1024)
if not block:
break
(2)通過(guò)readline,每次只讀一行。
while True:
line = f.readline()
if not line:
break

3. 序列化和反序列化
- 序列化:將數(shù)據(jù)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成二進(jìn)制串的過(guò)程。
- 反序列化:將在序列化過(guò)程中所生成的二進(jìn)制串轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)或?qū)ο蟮倪^(guò)程。
序列化實(shí)例:
# encoding:utf-8
import pickle
# 使用pickle模塊將數(shù)據(jù)對(duì)象保存到文件
# 字符串
data0 = "hello world"
# 列表
data1 = list(range(20))[1::2]
# 元組
data2 = ("x", "y", "z")
# 字典
data3 = {"a": data0, "b": data1, "c": data2}
print(data0)
print(data1)
print(data2)
print(data3)
output = open("data.pkl", "wb")
# 使用默認(rèn)的protocol
pickle.dump(data0, output)
pickle.dump(data1, output)
pickle.dump(data2, output)
pickle.dump(data3, output)
output.close()

反序列化實(shí)例:
# encoding=utf-8
import pickle
# 使用pickle模塊從文件中重構(gòu)Python對(duì)象
pkl_file = open("data.pkl", "rb")
data0 = pickle.load(pkl_file)
data1 = pickle.load(pkl_file)
data2 = pickle.load(pkl_file)
data3 = pickle.load(pkl_file)
print(data0)
print(data1)
print(data2)
print(data3)
pkl_file.close()

可以看出運(yùn)行結(jié)果與序列化實(shí)例運(yùn)行的結(jié)果完全一致。