前兩天有個朋友向我求助,她在寫畢業論文時,不小心將論文裏的中文雙引號替換爲英文的了,各種原因導致無法回退,8萬多字的論文,眼看就要交了,該怎麼辦?
首先想到 word 自身的替換功能,倒是能查到,但是沒法動態替換,即只替換兩邊引號,而不換中間內容;
另外一種方案是,即用 VBA,通過編程來替換,雖說做過幾個項目,可好久不用,拾起費勁,再加上 VBA 中各種概念和用法,學習成本太高,放棄;
還有一種方案,即用 Python 操作 word,首先對 Python 更熟悉,另外一定有別人造好的輪子。果然,沒用多久找到了 python-docx Python 庫,文檔齊全,功能強大,用來解決替換問題不在話下。
開始之前,先簡單瞭解下 python-docx
python-docx 介紹
python-docx 是用於創建可修改 微軟 Word 的一個 python 庫,提供全套的 Word 操作,是最常用的 Word 工具
概念
使用前,先了解幾個概念:
Document
:是一個 Word 文檔 對象,不同於 VBA 中 Worksheet 的概念,Document 是獨立的,打開不同的 Word 文檔,就會有不同的 Document 對象,相互之間沒有影響Paragraph
:是段落,一個 Word 文檔由多個段落組成,當在文檔中輸入一個回車鍵,就會成爲新的段落,輸入 shift + 回車,不會分段Run
表示一個節段,每個段落由多個 節段 組成,一個段落中具有相同樣式的連續文本,組成一個節段,所以一個 段落 對象有個 Run 列表
例如有一個 Word,內容是:
則 結構這樣劃分:
第二個 段落(paragraph),沒有內容,所以 節段(run)爲空
安裝
可以用 pip 來安裝:
pip install python-docx
命令行中運行下面語句,如果沒有報錯,則說明安裝成功
$ python -c 'import docx'
小試牛刀
python-docx 安裝後,測試一下:
from docx import Document
document = Document()
paragraph = document.add_paragraph('Lorem ipsum dolor sit amet.')
prior_paragraph = paragraph.insert_paragraph_before('Lorem ipsum')
document.save(r"D:\test.docx")
引入 Document 類
定義一個新文檔對象 document
想文檔中插入一個段落(paragraph)
再在這個段落(paragraph)前插入另一個段落
最後調用文檔對象 document 的 save 保存文檔
用 Word 打開保存的 test.docx 就可以看到:
問題分析與解決
瞭解了 python-docx 的基本概念,開始着手解決問題,大體思路是:
讀取文檔內容
查找 英文引號 之間的內容
將找到的內容的 英文引號 換成 中文引號,並將內容替換回去
完成處理後將文檔另存
查找目標
首先要解決的是如何找到 英文引號之間的內容?
例如文檔內容有這麼一段:
...
對"基於需求的教育資源配
置系統觀"的研究,尤其是對"以學習者爲中心"和從"個性化學習"、"精準教學"視角出發的
教育資源配置問題提供了理論"支持\\以及"方向指導
...
對於英文引號來說不區分前引號和後引號,怎麼能保證配置到的不會是 "和從"
、"、"
以及 "以學習者爲中心"和從"個性化學習"、"精準教學"
或者 不會忽略兩個引號出現在上下行的情況?
重溫正則表達式,終於得到如下表達式:
'"(?:[^"])*"'
?:
:爲了取消圓括號模式配置過程的緩存,即不需要遇到一個符合的就結束匹配[^"]
:表示匹配的內容不能是"
,以避免貪婪匹配,即避免匹配成 從第一個"
開始一直到最後一個"
結束整體的意思是 配置兩個
"
之間的內容,且內容中不包括"
後來整理過程中,還發現另一種寫法:
'".*?"'
不過 .
不能匹配換行符\n
,堅持要用,需要使用 可選修飾符
re.S
:
import re
pattern = re.compile('".*?"', re.S)
re.findAll(pattern, text) # text 爲待查找字符串
引入 正則表達式模塊
re
re.S
爲可選標識修飾符,使 . 匹配包括換行在內的所有字符利用
findAll
查找所有匹配內容
關於 Python 正在表達式的更多用法參考文後參考鏈接
實現
查找問題解決了,做替換就方便多了:
from docx import Document
import re
doc = Document(r"D:\論文.docx")
restr = '"(?:[^"])*"'
for p in doc.paragraphs:
matchRet = re.findall(restr, p.text)
for r in matchRet:
p.text = p.text.replace(r, '“' + r[1:-1] + '”')
doc.save(r'D:\論文_修正.docx')
引入 Document 類,和正則表達式模塊
打開目標文檔,字符串前的
r
表示取消字符串轉義,即按原始字符產來解釋循環文檔的 段落(paragraph),對每個段落,用正則表達式進行匹配
循環對於匹配到的結果,將前後引號,換成中文引號,並替換 段落(paragraph)的
text
;其中r[1:-1]
表示截取從第二個位置(第一個位置是 0)到倒數第二個位置截取字符串,剛好去掉前後引號最後另存文檔
注意:python-docx 保存文檔時不會給出任何提示,會瞬間完成,所以另存是個穩妥的做法
完工,趕緊將替換好的文檔發過去……
還沒來得回味,她說:“非常感謝!那個~ 能不能再幫我生成個圖表目錄,這個必須要……”
好吧,能者多勞(神器在手),幹就完了……
強大的 python-docx
在上面小試牛刀中,介紹了插入段落(paragraph)的用法,下面在介紹一些 python-docx 的其他功能
爲了簡潔,下面例子中省略了 Document 類的引入和實例化代碼,document 爲 Document 的實例
添加標題
默認情況下添加的標題是最高一級的,即一級標題,通過參數 level
設定,範圍是 1 ~ 9,也有 0 級別,表示的是段落標題:
# 添加一級標題
document.add_heading('我是一級標題')
decument.add_heading('我是二級標題', level=2)
decument.add_heading('我是段落標題', level=0)
添加換頁
如果一個段落不滿一頁,需要分頁時,可以插入一個分頁符,直接調用會將分頁符插入到最後一個段落之後:
# 文檔最後插入分頁
document.add_page_break()
# 特定段落分頁
from docx.enum.text import WD_BREAK
paragraph = document.add_paragraph("獨佔一頁") # 添加一個段落
paragraph.runs[-1].add_break(WD_BREAK.PAGE) # 在段落的最後一個節段後添加分頁
表格操作
Word 文檔中經常會用到表格,python-docx 如何添加和操作表格呢?
# 添加一個 2×2 表格
table = document.add_table(rows=2, cols=2)
# 獲取第一行第二列單元格
cell = table.cell(0, 1)
# 設置單元格文本
cell.text = '我是單元格文字'
# 表格的行
row = table.rows[1]
row.cells[0].text = 'Foo bar to you.'
row.cells[1].text = 'And a hearty foo bar to you too sir!'
# 增加行
row = table.add_row()
更復雜點的例子:
# 表格數據
items = (
(7, '1024', '手機'),
(3, '2042', '筆記本'),
(1, '1288', '臺式機'),
)
# 添加一個表格
table = document.add_table(1, 3)
# 設置表格標題
heading_cells = table.rows[0].cells
heading_cells[0].text = '數量'
heading_cells[1].text = '編碼'
heading_cells[2].text = '描述'
# 將數據填入表格
for item in items:
cells = table.add_row().cells
cells[0].text = str(item[0])
cells[1].text = item[1]
cells[2].text = item[2]
添加圖片
添加圖片,即,爲 Word 裏 菜單中 插入 > 圖片 插入的功能,插入圖片爲原始大小:
document.add_picture('image-filename.png')
插入時設置圖片大小:
from docx.shared import Cm
# 設置圖片的跨度爲 10 釐米
document.add_picture('image-filename.png', width=Cm(10))
除了釐米,python-docx 還提供了 英寸(Inches),如設置 1英寸:
Inches(1.0)
樣式
樣式可以針對整體文檔(document)、段落(paragraph)、節段(run),月具體,樣式優先級越高
python-docx 樣式功能配置多樣,功能豐富,這裏對段落樣式和文字樣式做簡單介紹
段落樣式
段落樣式包括:對齊、列表樣式、行間距、縮進、背景色等,可以在添加段落時設定,也可以在添加之後設置:
# 添加一個段落,設置爲無序列表樣式
document.add_paragraph('我是個無序列表段落', style='List Bullet')
# 添加段落後,通過 style 屬性設置樣式
paragraph = document.add_paragraph('我也是個無序列表段落')
paragraph.style = 'List Bullet'
文字樣式
在前面 python-docx 文檔結構圖可以看到,段落中,不同樣式的內容,被劃分成多個 節段(Run),文字樣式是通過 節段(Run)來設置的
設置加粗/斜體
paragraph = document.add_paragraph('添加一個段落')
# 設置 節段文字爲加粗
run = paragraph.add_run('添加一個節段')
run.bold = True
# 設置 節段文字爲斜體
run = paragraph.add_run('我是斜體的')
run.italic = True
設置字體
設置字體稍微複雜些,例如設置一段文字爲 宋體
:
paragraph = document.add_paragraph('我的字體是 宋體')
run = paragraph.runs[0]
run.font.name = '宋體'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋體')
總結
python-docx 是個功能強大的 Word 庫,能實現幾乎所有在 Word 中操作,今天通過一個實例,介紹了 python-docx 的一些基本用法,限於篇幅,沒法展開討論更多內容,如果有興趣可以深入研究,說不定可以讓 Word 像 Markdown 一樣簡單。
參考
https://python-docx.readthedocs.io/en/latest/
https://www.runoob.com/python/python-reg-expressions.html
https://www.cnblogs.com/nixindecat/p/12157623.html
【代碼獲取方式】
識別文末二維碼,回覆:666
PS:公號內回覆「Python」即可進入 Python 新手學習交流羣,一起 100天計劃!
-END-
Python 技術
關於 Python 都在這裏