2020-02-09 Python文本處理

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())
代碼示例運(yùn)行結(jié)果

默認(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()
運(yùn)行結(jié)果

從上面的例子可以看出,以二進(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))
定位結(jié)果

基于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
file對(duì)象常用的函數(shù)

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()
運(yùn)行結(jié)果

反序列化實(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é)果

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容