19. 文件讀寫 19.1 文件操作 數據持久化,是將程式中的對象以數據的方式保存到磁碟上,在程式下次運行時,可以將數據從磁碟上恢復到記憶體中。數據持久化的方式有很多,而最為常見的方式是將數據以文件的形式保存。在Python中,可以通過內置函數的方法進行文件的讀、寫、刪除等操作。 19.1.1 文件的 ...
目錄
19. 文件讀寫
19.1 文件操作
數據持久化,是將程式中的對象以數據的方式保存到磁碟上,在程式下次運行時,可以將數據從磁碟上恢復到記憶體中。數據持久化的方式有很多,而最為常見的方式是將數據以文件的形式保存。在Python中,可以通過內置函數的方法進行文件的讀、寫、刪除等操作。
19.1.1 文件的基本操作
文件的基本操作比較多,如創建、刪除、修改許可權、寫入、讀取等等。
- 刪除、修改許可權:作用於文件本身,屬於系統級操作
- 讀取、寫入:是文件最常用的操作,作用於文件內容,屬於應用級操作
文件的系統級操作,一般使用Python中的os、sys等模塊。
19.1.2 讀寫文件的一般步驟
讀寫文件一般常分為3步,每一步可使用相應的方法
- 1.打開文件:使用open方法,返回一個文件對象
- 2.具體的讀寫操作:使用該文件對象的read/write等方法
- 3.關閉文件:使用該文件對象的close方法
一個文件,必須在打開之後才可以對其進行相應的操作,併在操作完成均完成進行關閉。
19.1.2.1 打開文件
打開文件是讀寫操作的第一步,其方法open的具體定義如下所示:
open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True)
比較關鍵的參數如下所示:
- file:打開的文件名,屬於字元串類型,但如果文件名含有特殊字元,需要進行轉義。
- mode:打開文件的方式,如只讀、只寫、讀寫、二進位等等產,預設為只讀。
- encoding:如直接保存為文件文本,需要指定編碼格式,不然會造成讀取文件文件出現亂碼
mode的詳細介紹如下所示:
mode | 含義 |
---|---|
r | 只讀,但文件必須存在 |
w | 只寫,如果文件已經存在,則覆蓋,不存在,則重新創建 |
a | 以只寫的文件打開文件,併在文件後追加內容,如果文件不存在,則創建新文件 |
b | 以二進位形式打開,不能單獨使用 |
+ | 以讀寫形式打開文件, 不能單獨使用 |
r+ | 以讀寫形式打開文件,文件必須存在,當寫入時,會清空原內容 |
w+ | 以讀寫形式打開文件,文件不存在則創建文件,如已經存在,當寫入時,會清空原內容 |
a+ | 以讀寫形式打開文件,文件不存在則創建文件,如已經存在,當寫入時,在文件後追加原內容 |
以上僅為常見的一些模式,實際應用還可使用組合模式,即同時使用多種模式來操作文件,如rb、wb、wb+、ab等
另外根據操作系統的不同,又可以分為文本模式和二進位模式,其主要區別如下所示:
- 在Windows系統中,文本模式下Windows平臺的末尾換行符為\r\n,在讀取時轉換為\n,而在寫入時又將\n轉換為\r\n。這種隱藏的行為對於文本文件是沒有問題的,但如果以文本模式打開二進位數據文件(如圖片、EXE程式等)則會發生問題,因改變了文件的具體內容。
- 在Unix/Linux系統中,末尾換行符為\n,因此兩者沒有區別
19.1.2.2 具體讀寫操作
通過open方法得到文件對象後,就可以對文件進行操作,常用的方式是讀和寫。
1.讀取文件
通過調用文件對象的read方法可以獲得文件的內容,示例代碼如下所示:
>>> fo=open(r"C:\Surpass\a.txt","r")
>>> s=fo.read()
>>> s
打開文件後,文件對象fo中的read方法,會將文件的全部內容一次性讀取到記憶體中。
2.寫入文件
將字元串寫入文件,可以調用文件對象的write方法,示例代碼如下所示:
>>> fo=open(r"C:\Surpass\a.txt","w")
>>> fo.write("Surpass")
如果文件是以二進位形式打開,則只能以二進位形式寫入數據。
>>> fo=open(r"C:\Surpass\a.txt","wb")
>>> fo.write(b"Surpass")
19.1.2.3 關閉文件
直接使用文件對象的close方法即可。在打開文件並全部操作完之後,需要及時關閉。否則會導致其他操作出錯,如刪除、移動等,則提示文件正在使用。
19.1.3 文件對象方法
常見的文件對象方法如下所示:
方法 | 描述 |
---|---|
read(size) | 讀取指定size的位元組數據,然後作為字元串或bytes對象返回,size為可選參數,如未指定,則預設文件所有內容 |
readline() | 讀取一行,併在字元串末尾留下換行符\n,如果到文件尾,則返回空字元串 |
readlines() | 讀取所有行,並保存至列表中,每個元素代表一行,類似於list(fo) |
writer(string) | 將string寫入到文件中,返回寫入的字元數,如果以二進位模式,則需要將string轉換為bytes對象 |
tell() | 返迴文件對象當前所在位置,從文件開頭開始計算位元組數 |
seek(offset,from_what) | 改變文件對象所處的位置。offset是相對參考位置的偏移量,from_what表示參考位置,0-文件頭,預設;1-當前位置;2-文件尾 |
19.1.4 文件對象迭代器
文件對象本身也是一個迭代器,可以與for迴圈配合進行文件的讀取。示例如下所示:
>>> f=open("a.txt","wb+")
>>> f.write(b"name is Surpass,age is 28\n")
25
>>> f.write(b"I am learning Python")
20
>>> f.close()
>>> f=open("a.txt","r+")
>>> for content in f:
... print(content)
name is Surpass,age is 28
I am learning Python
>>> f.close()
在for迴圈中,每迴圈一次,相當於調用了一次readline方法。
19.1.5 使用with簡化文件操作
通過以上你會發現,每次使用文件操作,都需要3個步驟。那有沒有簡便的辦法來簡化這些操作了?Python內置了with語句,使用其可以簡化這種寫法,在調用完成之後,with語句會自動關閉。其語法格式如下所示:
with 表達式 as 變數:
doSomething
針對文件操作而言,表達式就是open函數,as後面的變數就是open返回的文件對象。
示例代碼如下所示:
import os
filePath=os.getcwd()
filename="a.txt"
with open(os.path.join(filePath,filename),"wb+") as fo:
try:
fo.write(b"name is Surpass,age is 28\n")
fo.write(b"I am learning Python")
except Exception as ex:
print(f"write error\{ex}")
with open(os.path.join(filePath,filename),"r") as fo:
for content in fo:
print(content)
19.1.6 字元串與二進位的轉化
在處理文件操作時,常用的做法是以二進位形式保存,以文件方式使用。一是二進位文件更小,便於網路傳輸和存儲,另外也可以避免保存與讀取編碼不同造成的亂碼情況。
19.1.6.1 將字元串轉換為二進位數
bytes定義格式如下所示:
string, encoding[, errors]
示例代碼如下所示:
>>> tempA=b"name is Surpass,age is 28"
>>> tempB=bytes("name is Surpass,age is 28","utf8") # 使用utf8編碼
>>> print(f"{tempA}\n{tempB}")
b'name is Surpass,age is 28'
b'name is Surpass,age is 28'
19.1.6.2 將二進位數轉換為字元串
如果將二進位數轉換為字元串,可以調用二進位對象的decode方法,並傳入指定的解碼格式,示例如下所示:
>>> tempB=bytes("我愛中國,I love China","gb2312")
>>> print(f"{tempB}\n{tempB.decode('utf8')}\n{tempB.decode()}")
Traceback (most recent call last):
File "<input>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xce in position 0: invalid continuation byte
程式報錯了,這個問題仔細看看,就知道原因所在了,編碼使用gb2312,解碼使用了utf8。因此在做轉換時,需要避免這種情況發生,
>>> tempB=bytes("我愛中國,I love China","gb2312")
>>> print(f"{tempB}\n{tempB.decode('gb2312')}")
b'\xce\xd2\xb0\xae\xd6\xd0\xb9\xfa\xa3\xacI love China'
我愛中國,I love China
Windows平臺與Linux平臺編碼解碼是有區別的,主要如下所示:
- 在Linux平臺中,生成的文件預設編碼是utf8格式,所以解碼需要指定解碼格式應為utf8
- 在Windows平臺中,中文操作系統生成文件預設為gb2312/gbk格式,所以解碼需要指定為相應的編碼格式才能正常解碼
為避免因操作系統不同,因此在轉換時需要顯式指定相應的編碼和解碼格式
19.2 json讀寫
在日常種類介面測試中,會經常處理JSON格式的請求報文和響應報文等,平時用得最多的也是Python自帶的json包,其提供了4個方法dumps、dump、loads、load。
1、JSON不能存儲每一種Python值,僅能存儲以下數據類型的傳下
- 字元串
- 整形
- 浮點型
- 布爾型
- 列表
- 字典
- NoneType
2、JSON不能表示Python對象,如File對象、CSV Reader、Regex對象等
19.2.1 查看json的使用方法
import json
print(json.__all__)
輸出結果為:
['dump', 'dumps', 'load', 'loads', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder']
19.2.2 json 讀取
json包常用的讀取方法為loads、load。
loads:可理解為load string,其功能是將json格式的字元串轉換為Python數據類型(字典)
load:讀取json文件,將轉換為Python類型
以下分別介紹其用法
19.2.2.1 json.loads
loads常用用法如下所示:
json.loads(str,encoding='utf8')
示例如下所示:
import json
jsonStr="""{
"book":"json in action",
"author":"Surpassme",
"isbn":961839721541,
"中文名":"JSON實戰"
}"""
print(f"jsonStr type is {type(jsonStr)}")
if isinstance(jsonStr,(str,)):
result=json.loads(jsonStr,encoding="utf8")
print(f"輸出的json\n{result}")
else:
print(f"傳入的字元類型{type(jsonStr)},不是字元串")
輸出結果
jsonStr type is <class 'str'>
輸出的json
{'book': 'json in action', 'author': 'Surpassme', 'isbn': 961839721541, '中文名': 'JSON實戰'}
需要註意兩點:
1、傳入的數據一定要是字元串格式,如果傳入字典則會出錯
2、建議增加字元串編碼格式,防止出現亂碼
19.2.2.2 json.load
load常用用法如下所示:
json.load(jsonfile)
示例如下所示:
import json
import os
def ReadJsonFile(path,strEncode="utf8"):
try:
with open(path,"r",encoding=strEncode) as fr:
return json.load(fr)
except Exception as ex:
raise ex
if __name__=="__main__":
jsonFile=os.getcwd()+"\\jsonfile.json"
data=ReadJsonFile(jsonFile)
print(f"data is\n{data}")
輸出結果
data is
{'book': 'json in action', 'author': 'Surpassme', 'isbn': 961839721541, '中文名': 'JSON實戰'}
19.2.3 json保存
json包常用的讀取方法為dumps、dump。
dumps:可理解為dump string,其功能是將Python數據類型將轉換為json格式的字元串
dump:將Pyhon數據保存為json文件
19.2.3.1 dumps
dumps常用用法如下所示:
json.dumps(obj,ensure_ascii=True,indent=None)
- ensure_ascii:輸出字元串是否採用ascii編碼,如果有中文,需要使用utf8編碼
- indent:輸出美化功能,一般為正數才有效
示例如下所示:
import json
jsonStr={
"book":"json in action",
"author":"Surpassme",
"isbn":961839721541,
"中文名":"JSON實戰"
}
result=json.dumps(jsonStr,ensure_ascii=False,indent=1)
print(result)
運行結果如下所示:
{
"book": "json in action",
"author": "Surpassme",
"isbn": 961839721541,
"中文名": "JSON實戰"
}
19.2.3.2 dump
dump常用用法如下所示:
json.dump(obj,file,ensure_ascii=True,indent=None)
示例如下所示:
import json
def SavaAsJsonFile(path,data,strEncode="utf8",ensure_ascii=False,indent=None):
try:
with open(path,"a",encoding=strEncode) as fw:
return json.dump(data,fw,ensure_ascii=ensure_ascii,indent=indent)
except Exception as ex:
raise ex
if __name__=="__main__":
jsonStr={
"book":"json in action",
"author":"Surpassme",
"isbn":961839721541,
"中文名":"JSON實戰"
}
path=os.getcwd()+"\\jsonfile.json"
SavaAsJsonFile(path,jsonStr,indent=1)
運行結果如下所示:
{
"book": "json in action",
"author": "Surpassme",
"isbn": 961839721541,
"中文名": "JSON實戰"
}
19.3 csv讀寫
csv(Comma-Separated Values)一般是特指以逗號做為分隔符的文本文件。因使用簡單方便,平時在測試過程也會經常用到該類型文件。今天就來學習一下在Python中如何處理csv文件。
19.3.1 CSV讀取
19.3.1.1 常規讀取
讀取CSV文件常用的步驟為,創建一個CSV文件對象,打開文件進行讀取
- CSV文件如下所示:
- 示例代碼如下所示:
import csv
import os
def ReadCSVFile(path=os.getcwd(),fileName="test.csv",fileEncode="utf8"):
csvFilePath=path+"\\"+fileName
dataValue=[]
if os.path.exists(csvFilePath) and os.path.isfile(csvFilePath):
try:
with open(csvFilePath,"r",encoding=fileEncode) as fr:
dataContent=csv.reader(fr)
for dataRow in dataContent:
dataValue.append(dataRow)
except Exception as ex:
raise ex
return dataValue
if __name__=="__main__":
path=r"C:\Users\Administrator\PycharmProjects\TestProject\PythonIOTest\csvLesson"
fileName="CSVTestFile.csv"
data=ReadCSVFile(path=path,fileName=fileName)
for item in data:
print(item)
- 運行結果如下所示:
['ID', 'Department', 'Employees', 'HireDate']
['1', 'Dev', 'Kevin', '2019-10-30']
['2', 'Prd', 'Lily', '2019-10-31']
['3', 'Test', 'Kate', '2019-11-01']
['4', 'Dev', 'Leo', '2019-11-02']
['5', 'Prd', 'Lucy', '2019-11-03']
['6', 'Test', 'Bruce', '2019-11-04']
['7', 'Dev', 'KK', '2019-11-05']
['8', 'Dev', 'Gaga', '2019-11-06']
['9', 'Dev', 'ABC', '2019-11-07']
['10', 'Dev', 'HBO', '2019-11-08']
19.3.1.2 字典形式讀取
如果CSV文件第一行為標題行,餘下全部為數據,則可以採用字典形式進行讀取。 以此種方式讀取時,會預設將第一行(標題)做為Key值,從第二行開始做為數據內容即Value
- 示例代碼如下所示:
import csv
import os
def ReadFromDict(path=os.getcwd(),fileName="test.csv",fileEncode="utf8"):
csvFilePath=path+"\\"+fileName
dataValue=[]
if os.path.exists(csvFilePath) and os.path.isfile(csvFilePath):
try:
with open(csvFilePath,"r",encoding=fileEncode) as fr:
dataContent=csv.DictReader(fr)
headers=dataContent.fieldnames
next(dataContent)
for dataRow in dataContent:
dataValue.append(dataRow)
except Exception as ex:
raise ex
return dataValue,headers
if __name__=="__main__":
path=r"C:\Users\Administrator\PycharmProjects\TestProject\PythonIOTest\csvLesson"
fileName="CSVTestFile.csv"
data,headers=ReadFromDict(path=path,fileName=fileName)
print(f"header is {headers}")
for item in data:
outStr=f'ID is {item.setdefault("ID","Exception")},Employee name is {item.setdefault("Employees", "Exception")} \
employees\'s department {item.setdefault("Department","Exception")}'
print(outStr)
- 運行結果如下所示:
header is ['ID', 'Department', 'Employees', 'HireDate']
ID is 1,Employee name is Kevin employees's department Dev
ID is 2,Employee name is Lily employees's department Prd
ID is 3,Employee name is Kate employees's department Test
ID is 4,Employee name is Leo employees's department Dev
ID is 5,Employee name is Lucy employees's department Prd
ID is 6,Employee name is Bruce employees's department Test
ID is 7,Employee name is KK employees's department Dev
ID is 8,Employee name is Gaga employees's department Dev
ID is 9,Employee name is ABC employees's department Dev
ID is 10,Employee name is HBO employees's department Dev
19.3.2 CSV寫入
19.3.2.1 常規寫入
讀取CSV文件常用的步驟為,創建一個CSV文件對象,打開文件進行寫入
- 示例代碼如下所示:
import csv
import os
def ReadCSVFile(path=os.getcwd(),fileName="test.csv",fileEncode="utf8"):
csvFilePath=path+"\\"+fileName
dataValue=[]
if os.path.exists(csvFilePath) and os.path.isfile(csvFilePath):
try:
with open(csvFilePath,"r",encoding=fileEncode) as fr:
dataContent=csv.reader(fr)
for dataRow in dataContent:
dataValue.append(dataRow)
except Exception as ex:
raise ex
return dataValue
def SaveCSVFile(path=os.getcwd(),fileName="testSave.csv",fileEncode="utf8",content=""):
csvFilePath=path+"\\"+fileName
if os.path.exists(path) and os.path.isdir(path):
try:
with open(csvFilePath,'w+',encoding=fileEncode,newline="") as fw:
dataObj=csv.writer(fw)
for item in content:
dataObj.writerow(item)
except Exception as ex:
raise ex
if __name__=="__main__":
path=r"C:\Users\Administrator\PycharmProjects\TestProject\PythonIOTest\csvLesson"
print("Test save file as csv file")
data=[
['ID', 'Department', 'Employees', 'HireDate'],
['1', 'Dev', 'Kevin', '2019-10-30'],
['2', 'Prd', 'Lily', '2019-10-31'],
['3', 'Test', 'Kate', '2019-11-01'],
['4', 'Dev', 'Leo', '2019-11-02'],
['5', 'Prd', 'Lucy', '2019-11-03']
]
SaveCSVFile(path=path,content=data)
dataFromSaveFile=ReadCSVFile(path=path,fileName="testSave.csv")
for item in dataFromSaveFile:
print(item)
1、如果保存的CSV文件出現空白行,則在with open(...,newline="")增加參數newline=""
2、CSV.Writer中的方法writerow()方法接受一個列表參數。列表中的每個詞,放在輸出的CSV文件中的一個單元格中。writerow()函數的返回值,是寫入文件中這一行的字元數(包含換行符)
- 運行結果如下所示:
Test save file as csv file
['ID', 'Department', 'Employees', 'HireDate']
['1', 'Dev', 'Kevin', '2019-10-30']
['2', 'Prd', 'Lily', '2019-10-31']
['3', 'Test', 'Kate', '2019-11-01']
['4', 'Dev', 'Leo', '2019-11-02']
['5', 'Prd', 'Lucy', '2019-11-03']
19.3.2.2 字典形式寫入
如果我們要創建帶有標題和數據的CSV文件,則可以採用以字典形式進行保存文件。
- 示例代碼如下所示:
import csv
import os
def ReadFromDict(path=os.getcwd(),fileName="test.csv",fileEncode="utf8"):
csvFilePath=path+"\\"+fileName
dataValue=[]
if os.path.exists(csvFilePath) and os.path.isfile(csvFilePath):
try:
with open(csvFilePath,"r",encoding=fileEncode) as fr:
dataContent=csv.DictReader(fr)
headers=dataContent.fieldnames
next(dataContent)
for dataRow in dataContent:
dataValue.append(dataRow)
except Exception as ex:
raise ex
return dataValue,headers
def SaveCSVFileUseDict(path=os.getcwd(),fileName="testSave.csv",fileEncode="utf8",headers="",content=""):
csvFilePath=path+"\\"+fileName
if os.path.exists(path) and os.path.isdir(path):
try:
with open(csvFilePath,'w+',encoding=fileEncode,newline="") as fw:
dataObj=csv.DictWriter(fw,fieldnames=headers)
dataObj.writeheader()
for item in content:
dataObj.writerow(item)
except Exception as ex:
raise ex
if __name__=="__main__":
path=r"C:\Users\Administrator\PycharmProjects\TestProject\PythonIOTest\csvLesson"
fileName="CSVTestFile.csv"
print("Test save file as csv file")
headers={"ID", "Name", "Author", "ISBN"}
data=[
{"ID":"1","Name":"Python基礎教程","Author":"Surpassme","ISBN":1088021365},
{"ID":"2","Name":"Java基礎教程","Author":"Surpassme","ISBN":2088021365},
{"ID":"3","Name":"C#基礎教程","Author":"Kevin","ISBN":3088021365},
]
SaveCSVFileUseDict(path=path,headers=headers,content=data)
dataFromSaveFile,headers=ReadFromDict(path=path,fileName="testSave.csv")
print(f"header is {headers}")
for item in dataFromSaveFile:
print(item)
以字典形式保存時,需要註意標題即為字典的Key值
- 運行結果如下所示:
Test save file as csv file
header is ['ID', 'Name', 'ISBN', 'Author']
OrderedDict([('ID', '2'), ('Name', 'Java基礎教程'), ('ISBN', '2088021365'), ('Author', 'Surpassme')])
OrderedDict([('ID', '3'), ('Name', 'C#基礎教程'), ('ISBN', '3088021365'), ('Author', 'Kevin')])
19.3.2.3 自定義分隔符和終止符
如果希望可以自定義分隔符(如Tab),希望有兩倍行距,則可以使用delimiter和lineterminator關鍵字參數。
- 示例代碼如下所示:
import csv
import os
def SaveCSVFile(path=os.getcwd(),fileName="test.csv",content="",delimiter=",",lineterminator="\n",fileEncoding="utf8"):
csvFilePath=path+"\\"+fileName
if os.path.exists(path) and os.path.isdir(path):
try:
with open(csvFilePath,"w+",encoding=fileEncoding,newline="") as fw:
dataObj=csv.writer(fw,delimiter=delimiter,lineterminator=lineterminator)
for item in content:
dataObj.writerow(item)
except Exception as ex:
raise ex
if __name__=="__main__":
path=r"C:\Users\Surpass\PycharmProjects\PythonIOTest\csvLesson"
print(f"Test save csv file")
data=[
['ID', 'Department', 'Employees', 'HireDate'],
['1', 'Dev', 'Kevin', '2019-10-30'],
['2', 'Prd', 'Lily', '2019-10-31'],
['3', 'Test', 'Kate', '2019-11-01'],
['4', 'Dev', 'Leo', '2019-11-02'],
['5', 'Prd', 'Lucy', '2019-11-03']
]
SaveCSVFile(path=path,content=data,delimiter="\t",lineterminator="\n\n")
- 運行結果如下所示:
19.3.3 示例項目
假設現在有一個任務,從一個文件夾中刪除所有CSV文件的第一行。主要方法如下所示:
- 依次逐個打開CSV文件,刪除第一行數據,再保存
- 在網上找找有沒有相應的工具
- 自己使用代碼編寫工具
今天我們就來嘗試用Python來解決該任務。先來分析一下,使用代碼需要解決的問題點有哪些
- 過濾到文件為CSV類型的文件
- 讀取每個文件的全部內容
- 刪除CSV文件的第一行,並保存為CSV文件
在使用工具或代碼來修改文件時,需要將數據或文件進行備份
詳細示例如下所示:
import csv
import os
import shutil
def GetCSVFileList(path,extName=".csv"):
"""
獲取CSV文件列表
"""
csvFileList=[]
for r,s,fs in os.walk(path):
for csvFile in fs:
if os.path.isfile(os.path.join(r,csvFile)) and os.path.splitext(os.path.join(r,csvFile))[-1] in extName:
csvFileList.append(os.path.join(r,csvFile))
return csvFileList
def ReadCSVFile(csvFileList,encoding="utf8"):
"""讀取CSV文件"""
csvRows=[]
for csvFile in csvFileList:
csvFilePath=(os.path.split(csvFile))[0]+"\\headerRemoved\\"+os.path.basename(csvFile)
try:
with open(csvFile,"r",encoding=encoding) as fr:
csvObj=csv.reader(fr)
for row in csvObj:
if csvObj.line_num==1:
continue
csvRows.append(row)
WriterCSVFile(csvFilePath,csvRows)
csvRows.clear()
except Exception as ex:
raise ex
def WriterCSVFile(path,data,encoding="utf8"):
"""保存CSV文件"""
try:
with open(path,"w+",encoding=encoding,newline="") as fw:
csvWriteObj=csv.writer(fw)
for row in data:
csvWriteObj.writerow(row)
except Exception as ex:
raise ex
def SaveAsDirectory(path,dirName):
"""創建另存文件夾"""
tempDir=path+"\\"+dirName
try:
if os.path.isdir(tempDir) and os.path.exists(tempDir):
shutil.rmtree(tempDir)
os.makedirs(tempDir,exist_ok=True)
else:
os.makedirs(tempDir,exist_ok=True)
except Exception as ex:
raise ex
if __name__=="__main__":
path=r"C:\Users\Surpass\PycharmProjects\PythonIOTest\csvLesson\CSVFile"
SaveAsDirectory(path,"headerRemoved")
csvFiles=GetCSVFileList(path)
ReadCSVFile(csvFiles)
最終的運行效果如下所示:
19.4 YAML讀寫
19.4.1 YAML簡介
YAML是YAML Ain't Markup Language的遞歸縮寫,意思其實是:"Yet Another Markup Language"。YAML 的語法和其他高級語言類似,並且可以簡單表達清單、散列表,標量等數據形態。它使用空白符號縮進和大量依賴外觀的特色,特別適合用來表達或編輯數據結構、各種配置文件、傾印調試內容、文件大綱,其擴展名為.yml.
19.4.2 基本語法
- 區分大小寫
- 使用縮進表示層級關係,同層元素左對齊
- 縮進時不允許使用Tab鍵,只允許使用空格。
- 縮進的空格數目不重要,只要相同層級的元素左側對齊即可
- 字串一般不使用引號,但必要的時候可以用引號框住
- 使用雙引號表示字串時,可用****進行特殊字元轉義
- #表示註釋
19.4.3 數據類型
- 對象:鍵值對的集合,又稱為映射(mapping)/ 哈希(hashes) / 字典(dictionary)
- 數組:一組按次序排列的值,又稱為序列(sequence) / 列表(list)
- 純量(scalars):單個的、不可再分的值
19.4.3.1 對象
- 對象通常是鍵值對形式(key: value),冒號後面要加一個空格。示例如下所示:
key:
ckey1: cvalue1
ckey2: cvalue2
ckey3:
cckey1: ccvalue1
cckye2: ccvalue2
ckey4: cvalue4
或
key: {ckey1: cvalue1, ckey2: cvalue2,ckey3: {cckey1: ccvalue1, cckye2: ccvalue2}, ckey4: cvalue4}
- 使用?+空格表示覆雜鍵,當鍵是一個列表或鍵值表時,就需要使用該符號進行表示。示例如下所示:
?
- Red
- Green
- Blue
:
- Color
以上等價於
{[blue, reg, green]: Color}
19.4.3.2 數組
以 - 開頭的行表示構成一個數組
- 一維數組
- Red
- Green
- Blue
以上等價於
[ 'Red', 'Green', 'Blue' ]
- 多維數組
-
- Red
- Green
- Blue
-
- apple
- tree
- ocean
以上等價於
[ [ 'Red', 'Green', 'Blue' ], [ 'apple', 'tree', 'ocean' ] ]
- 複雜結構
對象和數組可以組合成更為複雜的結構,示例如下所示:
Person:
- sex:
- man
- woman
- color:
yellow: Asia
white: Europe
balck: Africa
country:
- China
- American
- South Korea
- Russia
City:
-
id: 1
name: shanghai
-
id: 2
name: beijing
以上等價於:
{
Person: [
{
sex: [ 'man', 'woman' ]
},
{
color:
{
yellow: 'Asia',
white: 'Europe',
balck: 'Africa'
}
}
],
country: [ 'China', 'American', 'South Korea', 'Russia' ],
City: [
{
id: 1,
name: 'shanghai'
},
{
id: 2,
name: 'beijing'
}
]
}
19.4.3.3 純量
純量是最基本,不能再分割的值,如下所示:
- 字元串
- 布爾值
- 整數
- 浮點數
- Null
- 時間
- 日期
示例如下所示:
- 字元串
str:
- test str
- "test \n double " # 可以使用雙引號或者單引號包裹特殊字元,雙引號不會對特殊字元轉義。
- 'test \n double'
- line
newline # 字元串可以拆成多行,每一行會被轉化成一個空格
- 'testor'' day' # 單引號之中如果還有單引號,必須連續使用兩個單引號轉義。
test: | # 多行字元串可以使用|保留換行符,也可以使用 > 摺疊換行
def
foo
python: >
def
foo
textblock1: |+ # + 表示保留文字塊末尾的換行,- 表示刪除字元串末尾的換行。
def
foo
textblock2: |-
def
foo
顯示結果如下所示:
str:
[ 'test str',
'test \n double ',
'test \\n double',
'line newline',
'testor\' day' ],
test: 'def\nfoo\n',
python: 'def foo \n',
textblock1: 'def\n\nfoo\n\n',
textblock2: 'def \n\nfoo' }
- 布爾值
isMatch:
- true # true,True都可以
- False # false,False都可以
顯示結果如下所示:
isMatch: [ true, false ]
- 整數與浮點數
intNum:
- 123
- 0b11100011
- 0x12A
floatNum:
- 123.25
- 3.1415392e+5
顯示結果如下所示:
intNum: [ 123, 227, 298 ]
floatNum: [ 123.25, 314153.92 ]
- Null
isNull: ~ # 使用~表示null
顯示結果如下所示:
isNull: null
- 時間與日期
datetime: 2020-01-19T10:34:30+08:00 # 時間使用ISO 8601格式,時間和日期之間使用T連接,最後使用+代表時區
date: 2020-01-19 # 日期必須使用ISO 8601格式,即yyyy-MM-dd
time: 10:34:30
顯示結果如下所示:
datetime: Sun Jan 19 2020 10:34:30 GMT+0800 (中國標準時間),
date: Sun Jan 19 2020 08:00:00 GMT+0800 (中國標準時間),
time: 38070,
19.4.4 引用
錨點&和別名*,可以用來引用,示例如下所示:
server: &server
host: 10.68.1.81
username: root
password: password
test:
<<: *server
datebase: test
dev:
<<: *server
database: dev
rel:
<<: *server
database: rel
最終顯示的結果如下所示:
{
server:
{
host: '10.68.1.81',
username: 'root',
password: 'password'
},
test:
{
host: '10.68.1.81',
username: 'root',
password: 'password',
datebase: 'test'
},
dev:
{
host: '10.68.1.81',
username: 'root',
password: 'password',
database: 'dev'
},
rel:
{
host: '10.68.1.81',
username: 'root',
password: 'password',
database: 'rel'
}
}
& 用來建立錨點(server),<< 表示合併到當前數據,* 用來引用錨點。
19.4.5 應用場景
- 1、實現簡單,解析方便,特別適合在腳本語言中使用
- 2、配置文件,寫YAML比XML/ini/JSON快,因為不需要關註標簽、引號、括弧等
19.4.6 線上驗證網址:
http://www.bejson.com/validators/yaml/
19.4.7 YAML的Python讀寫
19.4.7.1 YAML庫安裝
在Python常用的讀寫YAML庫有pyyaml和ruamel
- pyyaml安裝
pip install -U pyyaml
或
pip3 install -U pyyaml
- ruamel安裝
pip install -U ruamel.yaml
或
pip3 install -U ruamel.yaml
19.4.7.2 Python寫YAML
19.4.7.2.1 將字典寫入YAML文件
示例代碼如下所示:
import os
import yaml
def SaveDict2YAML(path,filename,**data):
savePath=os.path.join(path,filename)
with open(savePath,mode="w",encoding="utf8") as fo:
yaml.dump(data,fo,Dumper=yaml.Dumper)
if __name__=="__main__":
testDict={
"server":
{
"host": "10.68.1.81",
"username": "root",
"password": "password"
},
"ower":["test","dev","rel"]
}
path=os.getcwd()
filename="dict2yaml.yaml"
SaveDict2YAML(path,filename,data=testDict)
最終保存的文件如下所示:
data:
ower:
- test
- dev
- rel
server:
host: 10.68.1.81
password: password
username: root
19.4.7.2.2 將列表寫入YAML文件
示例代碼如下所示:
import os
import yaml
def SaveList2YAML(path,filename,data):
savePath=os.path.join(path,filename)
with open(savePath,mode="w",encoding="utf8") as fo:
yaml.dump(data,fo,Dumper=yaml.Dumper)
if __name__=="__main__":
testList=[
"test",
"dev",
"rel",
{
"server":
{
"host": "10.68.1.81",
"username": "root",
"password": "password"
}
}
]
path=os.getcwd()
filename="list2yaml.yaml"
SaveList2YAML(path,filename,testList)
最終保存的文件如下所示:
- test
- dev
- rel
- server:
host: 10.68.1.81
password: password
username: root
19.4.7.3 Python讀YAML
import os
import yaml
import json
def ReadYAML(path,filename):
path=os.path.join(path,filename)
with open(path,mode="r",encoding="utf8") as fo:
data=yaml.load(fo.read(),Loader=yaml.Loader)
return data
if __name__=="__main__":
filename="list2yaml.yaml"
path=os.getcwd()
data=ReadYAML(path,filename)
print(json.dumps(data,indent=3))
最終的列印結果如下所示:
[
"test",
"dev",
"rel",
{
"server": {
"host": "10.68.1.81",
"password": "password",
"username": "root"
}
}
]
如果使用ruamel寫YAML文件,需要將Dumper更換一下即可,如下所示:
yaml.dump(data,fo,Dumper=ruamelyaml.RoundTripDumper)
19.5 Excel讀寫
19.5.1 安裝openpyxl模塊
Python沒有自帶openpyxl,需要自行安裝,安裝方法如下所示:
pip install -U openpyxl
驗證是否安裝成功
pip list | findstr "openpyxl"
或
pip show openpyxl
返回以下結果即說明安裝成功
openpyxl 3.0.0
19.5.2 讀取Excel文檔
以下示例將使用Excel表格 data.xlsx,使用Excel 2013創建,預設包含3個sheet頁,如下所示:
19.5.2.1 使用openpyxl打開Excel文檔
詳細代碼如下所示:
import openpyxl
import os
def getBaseDir(fileName):
return os.path.join(os.path.dirname(__file__),fileName)
def loadWorkbook():
workbook=openpyxl.load_workbook(getBaseDir("data.xlsx"))
print(type(workbook))
loadWorkbook()
openpyxl.load_workbook()接受文件名,返回一個workbook數據類型的值,這個workbook對象代表這個Excel文件。需要註意的是所打開的預設必須位於當前工作目錄,否則需要傳入完整路徑,可使用 os.getcwd()
輸出結果:
<class 'openpyxl.workbook.workbook.Workbook'>
19.5.2.2 從Workbook中獲取Sheet
import openpyxl
import os
def getBaseDir(fileName):
return os.path.join(os.path.dirname(__file__),fileName)
def loadWorkbook():
workbook=openpyxl.load_workbook(getBaseDir("data.xlsx"))
return workbook
def getSheet():
wb=loadWorkbook()
# 獲取所有sheet表名
print(wb.sheetnames,type(wb.sheetnames))
# 根據sheet名字獲取sheet
print(wb['Sheet2'],type(wb['Sheet2']))
# 獲取激活的sheet
print(wb.active,type(wb.active))
if __name__ == '__main__':
getSheet()
輸出結果:
['Sheet1', 'Sheet2', 'Sheet3'] <class 'list'>
<Worksheet "Sheet2"> <class 'openpyxl.worksheet.worksheet.Worksheet'>
<Worksheet "Sheet3"> <class 'openpyxl.worksheet.worksheet.Worksheet'>
19.5.2.3 從sheet頁中獲取單元格
import openpyxl
import os
def getBaseDir(fileName):
return os.path.join(os.path.dirname(__file__),fileName)
def loadWorkbook():
workbook=openpyxl.load_workbook(getBaseDir("data.xlsx"))
return workbook
def getSheet():
wb=loadWorkbook()
# 獲取所有sheet表名
print(wb.sheetnames,type(wb.sheetnames))
# 根據sheet名字獲取sheet
print(wb['Sheet2'],type(wb['Sheet2']))
# 獲取激活的sheet
print(wb.active,type(wb.active))
def getCellValue():
wb = loadWorkbook()
sheet=wb['Sheet2']
cell=sheet['A1']
# 獲取單元格的Value值
print("Row {} Column {} Value {} ".format(cell.row,cell.column,cell.value))
for i in range(1,10):
for j in range(1,3):
print(i,sheet.cell(row=i,column=j).value)
if __name__ == '__main__':
# getSheet()
getCellValue()
輸出結果:
Row 1 Column 1 Value A1-A1
1 A1-A1
1 B1-B1
2 A1-A2
2 B1-B2
3 A1-A3
3 B1-B3
4 A1-A4
4 B1-B4
5 A1-A5
5 B1-B5
6 A1-A6
6 B1-B6
7 A1-A7
7 B1-B7
8 A1-A8
8 B1-B8
9 A1-A9
9 B1-B9
19.5.2.4 從表中取得行和列
import openpyxl
import os
def getBaseDir(fileName):
return os.path.join(os.path.dirname(__file__),fileName)
def loadWorkbook():
workbook=openpyxl.load_workbook(getBaseDir("data.xlsx"))
return workbook
def getRowAndColumn():
wb = loadWorkbook()
sheet = wb['Sheet2']
print(tuple(sheet['A1':'B8']))
# 迴圈每一行
for r in sheet['A1':'B8']:
# 迴圈每一列
for c in r:
print("Locate is {},value is {}".format(c.coordinate,c.value))
if __name__ == '__main__':
# getSheet()
getRowAndColumn()
運行結果如下:
((<Cell 'Sheet2'.A1>, <Cell 'Sheet2'.B1>), (<Cell 'Sheet2'.A2>, <Cell 'Sheet2'.B2>), (<Cell 'Sheet2'.A3>, <Cell 'Sheet2'.B3>), (<Cell 'Sheet2'.A4>, <Cell 'Sheet2'.B4>), (<Cell 'Sheet2'.A5>, <Cell 'Sheet2'.B5>), (<Cell 'Sheet2'.A6>, <Cell 'Sheet2'.B6>), (<Cell 'Sheet2'.A7>, <Cell 'Sheet2'.B7>), (<Cell 'Sheet2'.A8>, <Cell 'Sheet2'.B8>))
Locate is A1,value is A1-A1
Locate is B1,value is B1-B1
Locate is A2,value is A1-A2
Locate is B2,value is B1-B2
Locate is A3,value is A1-A3
Locate is B3,value is B1-B3
Locate is A4,value is A1-A4
Locate is B4,value is B1-B4
Locate is A5,value is A1-A5
Locate is B5,value is B1-B5
Locate is A6,value is A1-A6
Locate is B6,value is B1-B6
Locate is A7,value is A1-A7
Locate is B7,value is B1-B7
Locate is A8,value is A1-A8
Locate is B8,value is B1-B8
19.5.3 寫入Excel文檔
19.5.3.1 創建和保存Excel文檔
import openpyxl
import os
def CreateNewWorkbook(path,fileName="test.xlsx"):
workbook=openpyxl.Workbook()
activeSheet=workbook.active
# 給sheet取名字
activeSheet.title="This is test sheet by openpyxl"
print(f"current acvtive sheet is {activeSheet},name is: {activeSheet.title}\nwork book is {workbook['This is test sheet by openpyxl']} ")
# 保存工作簿
workbook.save(path+"\\"+fileName)
if __name__ == "__main__":
path=r"C:\Users\Surpass\PycharmProjects\PythonIOTest\ExcelLesson"
CreateNewWorkbook(path,fileName="SaveAsByOpenpyxl.xlsx")
運行結果如下所示:
current acvtive sheet is <Worksheet "This is test sheet by openpyxl">,name is: This is test sheet by openpyxl
work book is <Worksheet "This is test sheet by openpyxl">
19.5.3.2 創建和刪除sheet
import openpyxl
import os
def CreateNewAndDelete(path,fileName="test.xlsx"):
workbook=openpyxl.Workbook()
print(f"init sheetname is:{workbook.sheetnames}")
# 創建Sheet
for i in range(5):
workbook.create_sheet(title="Sheet"+str(i),index=i)
print(f"create sheetname is {workbook.sheetnames}")
# 刪除Sheet
for j in range(3):
del workbook['Sheet'+str(j)]
print(f"after delete sheetname is:{workbook.sheetnames}")
# 保存Excel工作簿
workbook.save(path+"\\"+fileName)
if __name__ == '__main__':
path=r"C:\Users\Surpass\PycharmProjects\PythonIOTest\ExcelLesson"
CreateNewAndDelete(path,fileName="createOrDelSheet.xlsx")
運行結果如下所示:
init sheetname is:['Sheet']
create sheetname is ['Sheet0', 'Sheet1', 'Sheet2', 'Sheet3', 'Sheet4', 'Sheet']
after delete sheetname is:['Sheet3', 'Sheet4', 'Sheet']
19.5.3.3 將值寫入單元格
import openpyxl
import os
def CreateNewAndDelete(path,fileName="test.xlsx"):
workbook=openpyxl.Workbook()
for i in range(5):
workbook.create_sheet(title="Sheet"+str(i),index=i)
# 保存Excel工作簿
workbook.save(path+"\\"+fileName)
def InsertValutToExcel(path,fileName,sheetName,insertValue,cellRange):
filePath=path+"\\"+fileName
workbook=openpyxl.load_workbook(filePath)
sheetName=workbook[sheetName]
sheetName[cellRange]=insertValue
print(sheetName[cellRange].value)
workbook.save(filePath)
if __name__ == '__main__':
path=r"C:\Users\Surpass\PycharmProjects\PythonIOTest\ExcelLesson"
CreateNewAndDelete(path,fileName="createOrDelSheet.xlsx")
InsertValutToExcel(path=path,fileName="createOrDelSheet.xlsx",sheetName="Sheet3",insertValue="This is test value by openpyxl",cellRange="A3")
運行結果如下所示:
19.5.4 修飾Excel文檔
對某些單元格設置字體、樣式等,可以起到強調單元格的重要性等。因此需要從openpyxl.styles中導入Font()和Style()函數。
19.5.4.1 設置字體和樣式
設置單元格字體樣式主要使用Font對象,向其傳入關鍵字參數即可,主要關鍵字參數如下所示:
關鍵字參數 | 數據類型 | 描述 |
---|---|---|
name | string | 字體名稱,如Arial/Times New Roman |
size | int | 字體大小 |
italic | bool | 是否採用斜體,True代表使用斜體 |
bold | bool | 是否採用粗體,True代表使用粗體 |
underline | string | 是否帶下劃線 |
vertAlign | string | 垂直對齊方式 |
underline:為固定的參數可選項,如下所示:
- double:雙下劃線
- single:單下劃線
- doubleAccounting:會計雙下劃線
- singleAccounting:會計單下劃線
vertAlign:為固定的參數可選項,如下所示:- baseline:比較基準
- superscript:上標
- subscript:下標
示例代碼如下所示:
import os
from openpyxl import Workbook
from openpyxl.styles import colors
from openpyxl.styles import Font,Color
def SetExcelFont(path,fileName):
wb=Workbook()
sheet=wb.active
firstFontObj=Font(name="Arial",size="18",italic=True,bold=True,underline="single",color=colors.RED)
secondFontObj=Font(name="Times New Roman",size="24",bold=True,underline="double",vertAlign="baseline",color=colors.BLUE)
thirdFontObj=Font(name="Calibri",size="24",italic=False,bold=True,underline="doubleAccounting",vertAlign="superscript",color="0099CC00")
fourthFontObj=Font(name="Arial",size="34",italic=False,bold=False,underline="singleAccounting",vertAlign="subscript",color=colors.BLACK)
sheet["A1"].font=firstFontObj
sheet["B1"].font=secondFontObj
sheet["A2"].font=thirdFontObj
sheet["B2"].font=fourthFontObj
sheet["A1"]="hello"
sheet["B1"]="world"
sheet["A2"]="Software"
sheet["B2"]="Test"
wb.save(path+"\\"+fileName)
if __name__=="__main__":
path=r"C:\Users\Surpass\PycharmProjects\PythonIOTest\ExcelLesson"
SetExcelFont(path,fileName="SetExcelFont.xlsx")
運行結果如下所示:
19.5.4.2 添加公式
在Excel文件中,公式通常以=開始,通過其他單元格的計算得到值,使用openpyxl添加公式特別簡單,就像直接在Excel文件中添加公式一樣,現在有一份成績單,大於等於90,則評價為優,小於60為不合格,介於60和90為良好,示例代碼如下所示:
from openpyxl import load_workbook
from openpyxl import Workbook
from openpyxl.styles import Font,colors,PatternFill,fills
def AddFormula(path,fileName,sheetName="Sheet1"):
filePath=path+"\\"+fileName
wb=load_workbook(filePath)
ws=wb[sheetName]
for i in range(2,len(ws["B"])+1):
scorePost="B"+str(i)
formulaPos="C"+str(i)
formulaText=f'=IF(B{i}>=90,"優",IF(B{i}<60,"不合格","良好"))'
ws[formulaPos]=formulaText
if int(ws[scorePost].value) >=90:
# 寫入公式
ws[formulaPos].font=Font(name="Arial",color=colors.BLACK)
# 進行單元格填充
ws[formulaPos].fill=PatternFill(fill_type=fills.FILL_SOLID,fgColor=colors.GREEN)
elif int(ws[scorePost].value) <60:
ws[formulaPos].font=Font(name="Arial",color=colors.BLACK)
ws[formulaPos].fill=PatternFill(fill_type=fills.FILL_SOLID,fgColor=colors.RED)
else:
ws[formulaPos].font=Font(name="Arial",color=colors.BLACK)
ws[formulaPos].fill=PatternFill(fill_type=fills.FILL_SOLID,fgColor=colors.BLUE)
wb.save(filePath)
if __name__=="__main__":
path=r"C:\Users\Surpass\PycharmProjects\PythonIOTest\ExcelLesson"
AddFormula(path,fileName="AddFormula.xlsx")
運行的結果如下所示:
註意事項
- 如果在調用load_workbook()不帶參數data_only=True,則帶公式的單元格,在獲取單元格內容為其公式,如果僅希望獲取單元格值,則需要帶上data_only=True參數
19.5.4.3 調整行高和列寬
在Excel中,調整行高和列寬非常容易,今天我們來用代碼嘗試一下調整行高和列寬。主要涉及到Worksheet對象row_dimensions和column_demiensions。
示例代碼如下所示:
from openpyxl import Workbook
def SetHeightAndWidth(path,fileName="test.xlsx"):
filePath=path+"\\"+fileName
wb=Workbook()
ws=wb.active
ws["A1"]="Set Row Heigh"
ws["B1"]="Set Column Widht"
ws.row_dimensions[1].height=80
ws.column_dimensions['B'].width=50
wb.save(filePath)
if __name__=="__main__":
path=r"C:\Users\Surpass\PycharmProjects\PythonIOTest\ExcelLesson"
fileName="SetHeightAndWidth.xlsx"
SetHeightAndWidth(path,fileName=fileName)
運行結果如下所示:
19.6 對象序列化
在Python,如果需要將任意對象保存到磁碟中,必須要進行轉換為其相應的格式,如dict類型的數據是不能直接按文本格式保存的。在Python中,能實現任意對象與文本之間的相互轉化,同時也可以將任意對象與二進位之間相互轉化的稱為序列化,使用的模塊為pickle。
使用Python的pickle操作,可以將對象序列化字元串、文件等類似於文件的任意對象;也可以將這些字元串、文件或任意類似於文件的對象還原為原來的對象。
19.6.1 pickle模塊方法
pickle模塊中,常用的方法如下所示:
- dumps:將Python中的對象序列化二進位對象
- loads:從指定的pickle數據讀取並返回對象
- dump:將Python中的對象序列化二進位對象,並保存為文件
- load:讀取指定的序列化數據文件,並返回對象
以上4個方法又可以分為兩類:
- dumps和loads:是基於記憶體的Python對象與二進位相互轉化
- dump和load:是基於文件的Python對象與二進位相互轉化