檔案概念
檔案是存儲在外部存儲器中的一組資訊集合。
按照檔案的資料組織形式,檔案分為文本檔案和二進制檔案兩種。
文本檔案将資料視為字元,在檔案中儲存每個字元的編碼(ASCII, GBK, UTF-8等)。
常見文本檔案: 記事本檔案(.txt), 源代碼,網頁,日志等。
對于字元和編碼之間轉換的操作函數:
ord() 傳回字元對應的編碼
chr()
二進制檔案把資料的二進制值存儲到檔案中。
例如,有一個整數20190306,因為一個整數占4位元組,
是以把它儲存在外部存儲器中也要占4位元組。
将這個整數以二進制形式儲存到檔案中的結果
In: bin(20190306)
Out: '0b1001101000001010001100010'
二進制檔案:Word/Excel/圖檔/音樂等檔案。
文本檔案和二進制檔案存儲是不同的。
例如将 12 視為文本字元’12’,存儲時需要2個位元組,00110001 00110010 。
如視為整數(需4個位元組), 00000000 00000000 00000000 00001100。
檔案操作
操作檔案前一定要先打開檔案。内置函數open()可打開檔案,格式為 f = open(檔案名, 檔案模式, 編碼方式)
打開檔案後,借助檔案變量 f 來操作檔案。
中間得檔案模式的參數
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAnYldHL0FWby9mZvwFN4ETMfdHLkVGepZ2XtxSZ6l2clJ3LcV2Zh1Wa9M3clN2byBXLzN3btgHL9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsQTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yN2AjMyYGN3UjMjVWYzIWNzYzX0ADNxATM4IzLcBTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
打開檔案時需指定是文本檔案還是二進制檔案(“t"或"b”),預設為"t"。例如:
fw = open(r"d:\test.txt", “wt”) # w寫, t文本
該語句的作用是以“w”隻寫方式打開d盤根目錄下的文本檔案"test.txt"。
注:此處的r表示原始字元串,這樣\t就不會被視為轉義字元。如果沒有r, d:\test.txt 由于轉義不會被了解為正确的檔案名,将報錯。此處也可寫為open(“d:\test.txt”, “w”)
fr = open(r"d:\file1.txt", “r”) # 隻讀 , “r” 可省略
該語句的作用是以“r”隻讀方式打開d盤根目錄下的文本檔案"file1.txt",‘r’ 時要求檔案必須事先存在。
fbw = open(r"d:\file2.dat", “wb”) # b 較少用
該語句以“隻寫”方式打開d盤的二進制檔案"file2.dat"。
fr = open(r"d:\test.txt", "r", encoding="utf-8")
該語句以“隻讀”方式打開d盤下文本檔案"test.txt",指定編碼方式為"utf-8"。
注:Python預設按作業系統平台的編碼處理檔案。
Windows系統預設編碼為GBK,而test.txt檔案是用utf-8編碼儲存的,
是以打開該檔案時需指定這種編碼方式。如果不指定,或指定為其他編碼方式,
打開檔案後讀取時将報錯。對英文而言, utf-8和ascii是一樣的。記事本存盤時可選擇儲存編碼。
Python源檔案預設按utf-8編碼儲存,如果内含中文注釋,當用open打開時就應指定encoding='utf-8'。
GBK和utf-8的編碼方式是完全不一樣的,是以不同編碼方式打開會出錯
In: '中文'.encode('GBK')
Out: b'\xd6\xd0\xce\xc4' # GBK,1個漢字的編碼需2位元組
In: '中文'.encode('utf-8')
Out: b'\xe4\xb8\xad\xe6\x96\x87' # utf-8,1個漢字的編碼需3位元組
檔案對象的屬性
函數open()傳回一個可疊代的檔案對象,通過該對象對檔案進行操作。檔案對象常用屬性如下。
檔案對象的方法
關閉檔案
完成檔案操作後,一定要關閉檔案才能儲存修改并釋放檔案。在fw.write(“123”)此時資料隻是在緩存區,還沒有存入檔案的
關鍵字with可以自動管理資源,在退出with時會自動關閉檔案(省略f.close()語句)。with一般文法如下:
with open(檔案名, 檔案模式) as fp:
s = fp.read() # with語句結束時,會自動關閉檔案
目前檔案夾下有一個檔案log.txt,其内容如下:
第一行
第二行
編寫程式讀取其中的前6個字元。程式如下:
with open("./log.txt", "r") as f:
a = f.read(6) # 讀6個字元,不指定則讀取全部
print(a, end= '')
輸出如下:
第一行
第二
注意:
資料檔案如不指定儲存位置,則預設和程式存放在同一目錄中。
第一行後的換行符"\n"也算一個字元,是以 '第一行\n' 有4個字元。
/ 和 \ 都可以作為路徑分隔符。r'd:\tmp\f1.txt' 或 'd:/tmp/f1.txt' 都行。
#周遊檔案的行
with open("./log.txt", "r") as f:
a = f.readlines() # 讀取所有的行,得到字元串清單 a
for line in a:
print(line)
輸出結果如下:
第一行 # print()本身要換行,字元串内也含有'\n', 是以輸出時有空行
第二行
因為函數open()傳回的檔案對象是一個可疊代對象,是以用函數readlines()讀取檔案的語句可以省略。該段程式可簡寫為:
with open("./log.txt", "r") as f:
for line in f:
print(line)
文法糖格式 s = open("target.py",encoding='utf-8').read()
數值必須轉為字元串才能寫到文本檔案中。
例如:
随機生成一個長度為100的整數清單,其元素範圍為1~100,将該清單以每10個一行(元素之間以空格分隔)寫入文本檔案。(“d:\record.txt”)中。
from random import randint
a = [ ]
for i in range(100):
a.append(randint(1,100))
with open("d:\\record.txt", "w") as f:
str = ""
for i, v in enumerate(a):
str = str + "{} ".format(v) # 數值轉字元串,連接配接起來
if (i + 1) % 10 == 0: # 每滿10個寫入一行
b = f.write(str + "\n") # 寫入時自行添加換行符 '\n'
str = "" # 空字元串
寫完後想讀出來
b = [ ]
with open("d:\\record.txt") as f: # 檔案應存在
for line in f.readlines():
line = line.strip() # strip删除字元串兩側的空格符/換行符等
data = line.split() # split分解得到字元串清單 ['10', '23', ..... '89']
for v in data:
b.append(eval(v)) # 轉為整數,添加到清單
print(b)
讀寫二進制檔案
把記憶體中的資料對象寫入二進制檔案稱為序列化,從二進制檔案讀出并重建原資料對象稱為反序列化。這些操作可借助pickle子產品(自帶)。子產品中的dump()是寫入函數,load()是讀函數。寫入文法:pickle.dump(寫入對象, 檔案對象)
寫入對象:數值、字元串、清單、元組、字典等。
檔案對象:函數open()打開的檔案對象,将各類資料寫入其中。
寫:
import pickle
a = 1234 ; b = 3.14159 ; c = "程式" ; d = ['a', 'b', 'c'] ;
e = {"張":60, "王":70, "李":80}
with open("binary.dat", "wb") as f: # b不能省略
pickle.dump(a, f)
pickle.dump(b, f)
pickle.dump(c, f)
pickle.dump(d, f)
pickle.dump(e, f)
讀:
import pickle
with open("binary.dat", "rb") as f:
a = pickle.load(f) # 按存入的順序依次讀出
b = pickle.load(f)
c = pickle.load(f)
d = pickle.load(f)
e = pickle.load(f)
print(a, b, c, d, e)
二進制檔案的資料格式不統一,是以存入/讀出一般應使用同樣的庫。
dump()和load()會自動處理不同資料之間的邊界。這是簡單的資料持久化方法。
檔案定位
檔案指針用于标示檔案目前讀/寫位置。讀寫時,都從檔案指針的目前位置開始,根據讀寫的資料量向後移動檔案指針。檔案對象的函數tell()傳回檔案指針的目前位置。檔案剛打開時指向0位置。
檔案"log.txt"中儲存了字元串"python"。
with open("log.txt", "r") as f:
h = f.read(2)
print(h)
print("pos = {}".format(f.tell()))
j = f.read(2)
print(j)
print("pos = {}".format(f.tell()))
k = f.read(2)
print(k)
print("pos = {}".format(f.tell()))
結果是:
py
pos=2
th
pos=4
on
pos=6
檔案對象的seek()方法可移動檔案指針,
f.seek(偏移值 [,起點] ) 。
偏移值表示移動的距離;起點表示從哪裡開始移動:
0(預設值)表示從檔案頭開始;
1表示從目前位置開始;
2表示從檔案尾開始。
f.seek(10) # 移動到距離檔案頭部10個字元處
f.seek(4, 1) # 從目前位置再向後(尾部)移動4位元組
f.seek(-3, 1) # 從目前位置再向前(頭部)移動3位元組
f.seek(-6, 2) # 移到距離檔案尾部 6個位元組處
注:文本檔案僅支援起點0和正偏移值, 二進制檔案支援起點0/1/2。
讀/寫docx檔案和xlsx檔案
Python和Word結合可完成一些文檔自動生成的工作。包docx是讀寫word檔案的第三方包。安裝指令 pip install python-docx
建立新Word文檔及添加段落
(1) doc = Document() 建立一個新文檔對象doc。
(2) p = doc.add_paragraph(字元串) 建立一個段落對象p。
(3) run = p.add_run(字元串) 将産生一個Run對象,通過run對象設定排版格式。
(4) doc.save('檔案.docx')
這裡寫個例子
from docx import Document
from docx.shared import Pt, RGBColor # 字号,顔色
from docx.oxml.ns import qn # 字型
doc = Document() #生成一個空的docx對象
#設定字型需要這兩句
doc.styles['Normal'].font.name = '宋體' #全局預設字型
doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋體')
doc.add_heading('标題1', level=1) #添加标題
p = doc.add_paragraph() #添加段落
run=p.add_run('紅色宋體24磅')
run.font.size = Pt(24) #設定字号
run.font.color.rgb = RGBColor(255,0,0) #設定紅色, RGB(紅,綠,藍)
run = p.add_run('綠色微軟雅黑12磅')
run.font.name = '微軟雅黑' # 局部字型
run._element.rPr.rFonts.set(qn('w:eastAsia'), '微軟雅黑')
run.font.size = Pt(12) #設定字号
run.font.color.rgb = RGBColor(0,255,0) #設定綠色
run.bold = True; run.italic = True # 設定粗體和斜體
p2 = doc.add_paragraph('第2個段') #添加段落
doc.save('test.docx') #覆寫儲存到test.docx
結果:
讀取文檔的所有段落
Document對象的paragraphs屬性是一個包含文檔所有Paragraph對象的清單對象,一個Paragraph對象代表文檔的一個段落。對paragraphs屬性進行循環周遊可以操作文檔的所有段落。Paragraph對象的text屬性代表該段落的文字
from docx import Document
doc = Document("test.docx") # 檔案應存在
for p in doc.paragraphs:
print(p.text)
讀取文檔表格中的文字
Document對象的tables屬性是一個包含文檔所有Table對象的清單對象,一個Table對象代表文檔的一個表格。
Table對象的_cells屬性是一個包含表格所有單元格對象的清單。對表格的_cells屬性進行循環周遊可以操作表格的所有單元格。
單元格對象的text屬性代表該單元格的文字。
from docx import Document
doc = Document("Python.docx")
for t in doc.tables: # 隻含所有表,不含非表格内的文字
for c in t._cells: # 單個表的所有單元格
print(c.text) # 單元格内的文字
程式設計執行個體:
讀取檔案score.txt中的名單和成績(格式如下),為每個學生生成一個成績通知,通知都儲存在 stu.docx文檔中。
from docx import Document
from docx.shared import Pt,RGBColor
from docx.oxml.ns import qn
stu=[ ]
with open('score.txt') as f:
field = f.readline().split() # 第一行 列名
for line in f:
stu.append(line.split()) # 學生清單
doc = Document() #生成空文檔docx對象
doc.styles['Normal'].font.name = '宋體' #全局預設字型
doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋體')
for lst in stu:
doc.add_heading('成績通知單',level=1) # 添加标題
s=''
for i in range(3):
s = s +field[i]+':'+lst[i]+' ' # 學号 姓名 手機
doc.add_paragraph(s)
p = doc.add_paragraph() # 成績段
for i in range(3,5):
run = p.add_run(field[i]+':'+lst[i]+' ') # 兩門成績
if float(lst[i])<60:
run.font.color.rgb = RGBColor(255,0,0) #不及格則紅色
doc.add_paragraph() # 空行
doc.save('stu.docx') # 儲存到stu.docx
結果這樣
包openpyxl是讀寫xlsx檔案的第三方包,安裝指令為:
pip install openpyxl
步驟:
建立Excel工作簿和工作表
(1)wb =Workbook( ) # 新的工作簿
(2)wb.create_sheet("first") # 工作表
(3)wb.save('檔案.xlsx') # 儲存為Excel檔案
那麼工作簿就有兩張表,預設的Sheet表和first表
修改單元格的資料
要修改表格資料,需要先調用load_workbook()函數擷取工作簿。
有三種方法從工作簿中得到其中的一個工作表:
第一種是用Workbook對象的get_sheet_by_name方法(舊方法),其參數是工作表的名稱;
第二種是用Workbook對象的worksheets屬性,該屬性是一個Worksheet 對象清單,如ws = wb.worksheets[1];
第三種是通過索引方式,下标為工作表的名字,如ws=wb[‘first’]。
例子:
from openpyxl import Workbook, load_workbook
wb = load_workbook("test.xlsx") # 檔案存在且不能已被Excel打開,被Excel打開就被它鎖定着,
#其他的程式不能通路,而這個方法打開Excel表不會鎖着這個表,而是将它加載在記憶體裡面,然後釋放鎖
ws = wb["first"] # first表應存在
# 各種通路單元格的文法
ws['A1'] = "數學" # 行列坐标
ws['b1'] = "國文"
ws.cell(2, 1, 90) # 第2行第1列
ws.cell(2, 2, 91) # 第2行第2列
ws.append([80, 81])
ws['c2'] = "=sum(A2:B2)"
ws['c3'] = "=sum(A3:B3)"
wb.save("test.xlsx") # 儲存即更新檔案
結果:
讀取Excel單元格中的資料
擷取一個Cell對象後,通路Cell對象的value屬性即可讀取資料。
os子產品
import os
os.rename("wt.txt", "tmh\\wtt.txt")
将目前檔案夾下的檔案"wt.txt"改名并移動到子檔案夾"tmh"中。
os.remove("tmh\\wtt.txt" )
删除目前檔案夾的子檔案夾"tmh"中的檔案"wtt.txt"。
os.rmdir("tmh") 删除目前目錄下的子檔案夾tmh(要求為空)
os.mkdir("d:\\tmh") 在d盤根目錄下建一個檔案夾tmh
os.chdir("d:\\tmh") 把d:\\tmh設定為目前工作目錄
os.getcwd() 傳回目前工作目錄名
os.system('notepad.exe') 啟動記事本程式
os.startfile('1.2.mp4')
import shutil
shutil.rmtree('tmh') 删除子檔案夾tmh(不為空也可)
shutil.copyfile('log.txt', 'b.txt') 将log.txt複制得到b.txt
os.listdir(path)的功能是傳回path目錄下的檔案和目錄清單。對該清單進行遞歸周遊可以周遊檔案夾path下的所有檔案和子檔案夾。
os.path子產品
提供了關于路徑判斷、連接配接及切分的方法。
import os.path
os.path.dirname('D:\\Workspace\\tmh\\t7.txt')
傳回該路徑的檔案夾部分'D:\\Workspace\\tmh'。
os.path.basename('D:\\Workspace\\tmh\\t7.txt')
傳回路徑的最後一個組成部分't7.txt'
os.path.basename('D:\\Workspace\\tmh')
傳回該路徑的最後一個組成部分'tmh'
os.path.join('D:\\Workspace', 'tmh')
将兩個路徑連接配接成一個路徑,即'D:\\Workspace\\tmh'。
os.path.splitext('D:\\Workspace\\tmh\\t7.txt')
從路徑中分割出檔案的擴充名,它的傳回值是一個包含兩個元素的元組 ('D:\\Workspace \\tmh\\t7', '.txt')。
import os, sys, os.path
oldpath = r'D:\tmh'
newpath = r'D:\pic'
if not (os.path.exists(oldpath) and os.path.exists(newpath)):
print('目錄不存在')
sys.exit() # 退出程式
i = 0
for p in os.listdir(oldpath):
fpath = oldpath+'\\'+p # 完整路徑
if os.path.isfile(fpath):
fname, ext = os.path.splitext(p) # 主檔案名 擴充名
ext = ext.lower()
if ext in ('.png', '.jpg', '.jpeg'):
i += 1
newname = newpath+'\\'+str(i)+ext # 新的檔案路徑
os.rename(fpath, newname) # 重命名兼移動檔案
print('共修改檔案名 {} 個'.format(i))