天天看點

L:python的檔案操作

檔案概念

檔案是存儲在外部存儲器中的一組資訊集合。

按照檔案的資料組織形式,檔案分為文本檔案和二進制檔案兩種。

文本檔案将資料視為字元,在檔案中儲存每個字元的編碼(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 來操作檔案。

中間得檔案模式的參數

L:python的檔案操作

打開檔案時需指定是文本檔案還是二進制檔案(“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()傳回一個可疊代的檔案對象,通過該對象對檔案進行操作。檔案對象常用屬性如下。

L:python的檔案操作

檔案對象的方法

L:python的檔案操作

關閉檔案

完成檔案操作後,一定要關閉檔案才能儲存修改并釋放檔案。在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      

結果:

L:python的檔案操作

讀取文檔的所有段落

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文檔中。

L:python的檔案操作
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      

結果這樣

L:python的檔案操作

包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")   # 儲存即更新檔案      

結果:

L:python的檔案操作

讀取Excel單元格中的資料

擷取一個Cell對象後,通路Cell對象的value屬性即可讀取資料。

os子產品

L:python的檔案操作
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子產品

提供了關于路徑判斷、連接配接及切分的方法。

L:python的檔案操作
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))