程式化的 Word 文字搜尋與取代

最近在工作上會很需要經常使用 MS Word 對多份稿件進行大量的文字搜尋及取代,雖然早先已寫了一支 Python 程式來處裡,然而在後續檢查內容的過程裡,卻發現原先撰寫的取代功能處理得未盡確實,例如文字方塊(textbox)裡的內容就無法被取代掉。經過上網搜尋後找到了一項解法,儘管確實可行,不過 DR 仍覺得其背後的設計邏輯還蠻弔詭的。

 

原本的寫法是像這樣:

word.Selection.Find.Execute(FindText="Jack", 
    ReplaceWith="Stanley",                             
    Replace=win32com.client.constants.wdReplaceAll,
    MatchCase=True,
    MatchWildcards=False)
    

 

在 DR 的期望裡,Word 若在未指定任何範圍的情況下,應該要能夠做到全域的搜尋與取代。然而實際的執行結果並非如此,如前所述,例如文字方塊裡的內容就無法被取代到。而解決方案則是改從文件中的本文範圍(StoryRanges)著手,不過其弔詭之處就在於:這項作法的搜尋方法(Find.Execute)必須要用兩次,才能夠順利取代所有本文範圍中的內容。

 

以下是完整的範例程式碼(word_replace_all.py),該程式會開啟程式所在目錄中的「test.docx」文件,然後搜尋所有的「Jack」字串並取代為「Stanley」:

# -*- coding: utf-8 -*-
import os, sys
import win32com.client

from win32com.client import constants

def area_replace(area, find_text, replace_text):
    area.Find.Execute(FindText=find_text, 
        ReplaceWith=replace_text,                             
        Replace=constants.wdReplaceAll,
        MatchCase=True,
        MatchWildcards=False)

def word_replace_all():
    local_encoding = sys.getfilesystemencoding()
    appdir = os.path.abspath(os.path.dirname(sys.argv[0])).decode(local_encoding)
    os.chdir(appdir)
    
    filename = "test.docx"
    find_text = "Jack"
    replace_text = "Stanley"
    
    word = win32com.client.gencache.EnsureDispatch("Word.Application")
    word.Visible = False
    word.Documents.Open(os.path.abspath(filename))

    for story in word.ActiveDocument.StoryRanges:
        area_replace(story, find_text, replace_text)
        
        while(True):
            if story.NextStoryRange:
                story = story.NextStoryRange
                area_replace(story, find_text, replace_text)
            else:
                break

    word.ActiveDocument.Save()
    word.ActiveDocument.Close(False)
    word.Quit()

if __name__ == "__main__":
    word_replace_all()
    

 

為了突顯重點,上述的範例程式碼並不包含一些實務上會很需要的功能,例如一一開啟目錄中所有的 Word 文件,以及字串取代清單的讀取與套用。除了一般的內文以外,上述程式碼可以處理的區塊包含了文字方塊、註腳(footnote)、文字藝術師(WordArt)以及頁首頁尾(header & footer)。

 

Tags: