20 September, 2010

用 Python 處理 doc 檔

到今天已經退伍一個禮拜了, 如果還在當兵的話, 現在應該穿著全副武裝還有不能防水的雨衣在救災吧 XD

不久前我用 Python 寫了一個小程式幫女友處理她的 doc 檔,底下是心得記錄。 基本上 doc 檔比純文字檔難處理,因為要用到 windows API, 不過這也還好,大多數的人都用 windows, 所以 Python 一定會有可用的函式庫,而網路上還可以找到幾個用 Python 處理 doc 檔的 範例。 另一個棘手的地方是,doc 檔的段落具有格式,比如字型大小、顏色、粗細、框線、上下標, 段落還可能有縮排,在處理段落時這些都要保留下來。 如果是純文字就不會有這個問題, 通常只需 string 的內建函式即可搞定,甚至不用動到 regex。 之前沒接觸過 windows API,(平常我都用 LaTeX 或是純文字檔) 要如何解決這個問題,一開始一點頭緒都沒有, 後來才想到,如果要保留格式的話,用類似滑鼠選取之後的複製、貼上就可以了, 查了一下 MSDN,果然有這兩個函式。

我要處理的問題是將段落根據需求排序。 比方說每個段落開始都有一個數字,我想根據數字的大小來排列這些段落。 pos 這個變數的目的是儲存段落的範圍以便使用複製、貼上, pos 是一個 list,其元素是一個只有兩個元素的 list, 這兩個元素分別記錄了段落的起始與結束的行號。 接下來 order 這個變數是排序的根據, order 也是一個 list,其元素是一個只有兩個元素的 tuple, 第一個元素記錄了排序所依據的數字,第二個元素則是段落所在的指標 (就是第幾個段落)。 order.sort() 會將 order 這個 list 中的元素依大小排序 (事實上是先比第一個,再比第二個), 排完之後我們所需的段落順序就記錄在 order 中元素的第二個元素。 原始檔如下:


# -*- coding: utf-8 -*-
import win32com
from win32com.client import Dispatch, constants

msword = Dispatch('Word.Application')
msword.Visible = 0
msword.DisplayAlerts = 0
doc = msword.Documents.Open(FileName=u"E:\\input.doc")
new_doc = msword.Documents.Add()

N = len(doc.Paragraphs)
pos = []
order = []

try:
    p = 0
    for i in range(N):
        text = doc.Paragraphs(i).Range()
        k = text.find("-")
        if k != -1:
             if pos:
                 pos[-1][1] = i-1
             pos.append([i, 0])
             order.append((int(text[:k]), p))
             p += 1

    pos[-1][1] = i
    order.sort()

    for d in order:
         range = doc.Range(
                 doc.Paragraphs(pos[d[1]][0]).Range.Start,
                 doc.Paragraphs(pos[d[1]][1]).Range.End)
         range.Copy()
         j = len(new_doc.Paragraphs)
         new_doc.Paragraphs(j).Range.Paste()
         # 貼在新文件的最末端
         # 作法似乎很蠢,不過暫時想不到更好的作法

finally:
     new_doc.SaveAs('E:\\output.doc')
     new_doc.Close()
     doc.Close()
     msword.Quit()

No comments: