天天看點

利用python實作字音回填

作者:小小明

大家好,我是小小明,今天我要給大家分享的是兩個word文檔處理的案例,核心是讀取excel的資料,按照指定的規則寫入到word中。

之前們已經分享過一些pandas讀寫excel的例子,這次我們需要在此基礎上還需讀寫word文檔。

python-docx簡介

利用python讀寫word文檔的庫是python-docx,安裝:

pip install python-docx
           

複制

python-docx 官方文檔:https://python-docx.readthedocs.io/en/latest/

基本的對象關系:

利用python實作字音回填

單字字音分析處理

之前遇到一個需求:

利用python實作字音回填

分析需求呢,會發現它要求在word文檔中添加一行在excel對應的聲韻調,若音1聲超過1個字元還需将最後一個字元上标,音1韻不上标,音1調需整體上标。

為了解決這個問題,首先我們需要處理出我們需要的資料,每個單字對應的字音(音韻聲)。

自然第一步是讀取excel檔案,最終産生以調查條目為鍵,聲韻調作為值的字典,而音1聲超過1個字元需将最後一個字元上标,是以音1聲應該把不需上标和需要上标的分開存儲,最終形成一個四元組。

看看代碼吧:

資料讀取

import pandas as pd

df = pd.read_excel("老男單字.xls", usecols=[0, 1, 3, 4, 5], dtype={'編号': 'str'})
df.head()
           

複制

編号 調查條目 音1聲 音1韻 音1調
0001 t o 44
0002 th o 44
0003 t a 13
0004 l o 21
0005 ts o 35

單字對應的字音字典生成

symbols = {}
for row in df.values:
    k = row[1]
    a = row[2].strip()
    if len(a) > 1:
        v = (a[:-1], a[-1], row[3].strip(), str(row[4]))
    else:
        v = (a, "", row[3].strip(), str(row[4]))
    symbols[k] = v
           

複制

利用pandas檢視生成結果:

pd.DataFrame.from_dict(symbols, 'index').head(10)
           

複制

利用python實作字音回填

測試資料比對

好,接下來,我們嘗試讀取word文檔的第一個表格,并比對擷取每個字音需要添加的音韻調:

from docx import Document

doc = Document(r"01老男單字字音對照表(興義).docx")
header_cells = doc.tables[0].rows[0].cells[1:]
for cell in header_cells:
    k = cell.text[5]
    print(cell.text, symbols[k])
           

複制

列印結果:

0001多 ('t', '', 'o', '44')
0002拖 ('t', 'h', 'o', '44')
0003大~小 ('t', '', 'a', '13')
0004鑼 ('l', '', 'o', '21')
0005左 ('t', 's', 'o', '35')
0006歌 ('k', '', 'o', '44')
0007個 ('k', '', 'o', '13')
0008可 ('k', 'h', 'o', '13')
           

複制

說明資料比對暫時沒有問題。

測試添加資料

然後嘗試向第一個表格添加資料,并儲存看看效果:

from docx.enum.text import WD_ALIGN_PARAGRAPH

doc = Document(r"01老男單字字音對照表(興義).docx")
header_cells = doc.tables[0].rows[0].cells[1:]
row_cells = doc.tables[0].add_row().cells[1:]
for header_cell, row_cell in zip(header_cells, row_cells):
    k = header_cell.text[4]
    a, b, c, d = symbols[k]
    (p,) = row_cell.paragraphs
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.style.font.name = 'IPAPANNEW'
    p.add_run(a)
    p.add_run(b).font.superscript = True
    p.add_run(c)
    p.add_run(d).font.superscript = True
doc.save("tmp.docx")
           

複制

結果:

利用python實作字音回填

可以看到已經順利添加進去,并沒有什麼問題。

最終測試

現在改下代碼,添加所有單元格:

doc = Document(r"01老男單字字音對照表(興義).docx")
for t in doc.tables:
    header_cells = t.rows[0].cells[1:]
    row_cells = t.add_row().cells[1:]
    for header_cell, row_cell in zip(header_cells, row_cells):
        k = header_cell.text[4]
        a, b, c, d = symbols[k]
        (p,) = row_cell.paragraphs
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        p.style.font.name = 'IPAPANNEW'
        p.add_run(a)
        p.add_run(b).font.superscript = True
        p.add_run(c)
        p.add_run(d).font.superscript = True
doc.save("result.docx")
           

複制

結果:

利用python實作字音回填

可以看到,都順利添加了對應的字音,但有點不太滿意,有部分整行都是空白單元格,應該删除更佳。

增加删除空行的代碼:

doc = Document(r"01老男單字字音對照表(興義).docx")
for t in doc.tables:
    ## 從第四行開始檢查并去除表格的空白行
    for row in t.rows[3:]:
        if np.all([cell.text == "" for cell in row.cells]):
            t._tbl.remove(row._tr)
    ## 取出第一行從第二個開始所有單元格
    header_cells = t.rows[0].cells[1:]
    ## 取出新增一行從第二個開始所有單元格
    row_cells = t.add_row().cells[1:]
    for header_cell, row_cell in zip(header_cells, row_cells):
        k = header_cell.text[4]
        a, b, c, d = symbols[k]
        (p,) = row_cell.paragraphs
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        p.style.font.name = 'IPAPANNEW'
        p.add_run(a)
        p.add_run(b).font.superscript = True
        p.add_run(c)
        p.add_run(d).font.superscript = True
doc.save("result.docx")
           

複制

再次執行,office打開的結果:

利用python實作字音回填

可以看到空行已經都順利的被删除。

完整處理代碼

整個過程已經完整測試通過,最終完整處理代碼為:

from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx import Document
import pandas as pd
import numpy as np

df = pd.read_excel("老男單字.xls", usecols=[1, 3, 4, 5])
symbols = {}
for row in df.values:
    k = row[0]
    a = row[1].strip()
    if len(a) > 1:
        v = (a[:-1], a[-1], row[2].strip(), str(row[3]))
    else:
        v = (a, "", row[2].strip(), str(row[3]))
    symbols[k] = v

doc = Document(r"01老男單字字音對照表(興義).docx")
for t in doc.tables:
    ## 從第四行開始檢查并去除表格的空白行
    for row in t.rows[3:]:
        if np.all([cell.text == "" for cell in row.cells]):
            t._tbl.remove(row._tr)
    ## 取出第一行從第二個開始所有單元格
    header_cells = t.rows[0].cells[1:]
    ## 取出新增一行從第二個開始所有單元格
    row_cells = t.add_row().cells[1:]
    for header_cell, row_cell in zip(header_cells, row_cells):
        k = header_cell.text[4]
        a, b, c, d = symbols[k]
        (p,) = row_cell.paragraphs
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        p.style.font.name = 'IPAPANNEW'
        p.add_run(a)
        p.add_run(b).font.superscript = True
        p.add_run(c)
        p.add_run(d).font.superscript = True
doc.save("result.docx")
           

複制

詞彙音分析處理

需求2:

利用python實作字音回填

當然有部分詞彙存在兩個詞就需要換行都寫入:

利用python實作字音回填

上标規則是所有的數字和h都上标,其他不用上标。

那同樣的思路,先讀取excel并解析出需要的資料:

資料讀取并解析

import pandas as pd

df = pd.read_excel("詞彙(凱裡).xls", index_col=0)
df.tail()
           

複制

結果:

利用python實作字音回填
## 詞音按照不是h而且不是數字,以及是h,和是數字 三種規則切分
df['詞1音'] = df['詞1音'].str.findall("[^h\d]+|h|\d+")
df['詞2音'] = df['詞2音'].str.findall("[^h\d]+|h|\d+")
df = df[["詞1字", "詞1音", "詞2字", "詞2音"]]
df.tail()
           

複制

結果:

利用python實作字音回填

最終想得到的處理結果:

symbols = df.to_dict('index')
           

複制

word文檔編号轉換測試

運作以下代碼:

from docx import Document

doc = Document(r"02詞彙對照表(興義).docx")
i = 0
for t in doc.tables:
    ## 取出第一行從第二個開始所有單元格
    header_cells = t.rows[0].cells[1:]
    for header_cell in header_cells:
        k = int(header_cell.text[:4].replace('ʰ', '9'))
           

複制

未發現任何報錯,說明對應編号擷取成功,于是就可以通過将鍵k傳入symbols擷取需要寫入的資料。

最終word生成代碼

from docx import Document

doc = Document(r"02詞彙對照表(興義).docx")
for t in doc.tables:
    ## 取出第一行從第二個開始所有單元格
    header_cells = t.rows[0].cells[1:]
    ## 取出新增一行從第二個開始所有單元格
    row_cells = t.add_row().cells[1:]
    for header_cell, row_cell in zip(header_cells, row_cells):
        k = int(header_cell.text[:4].replace('ʰ', '9'))
        symbol_dict = symbols[k]
        if not pd.isna(symbol_dict['詞2字']):
            row_cell.add_paragraph()
        for i, p in enumerate(row_cell.paragraphs, 1):
            p.style.font.name = 'IPAPANNEW'
            p.add_run(symbol_dict[f'詞{i}字']).font.name = "宋體"
            for n, e in enumerate(symbol_dict[f'詞{i}音']):
                run = p.add_run(e)
                if n % 2 == 1:
                    run.font.superscript = True

doc.save("result.docx")
           

複制

完整處理代碼

from docx import Document
import pandas as pd

df = pd.read_excel("詞彙(凱裡).xls", index_col=0)
## 詞音按照不是h而且不是數字,以及是h,和是數字 三種規則切分
df['詞1音'] = df['詞1音'].str.findall("[^h\d]+|h|\d+")
df['詞2音'] = df['詞2音'].str.findall("[^h\d]+|h|\d+")
df = df[["詞1字", "詞1音", "詞2字", "詞2音"]]
symbols = df.to_dict('index')

doc = Document(r"02詞彙對照表(興義).docx")
for t in doc.tables:
    ## 取出第一行從第二個開始所有單元格
    header_cells = t.rows[0].cells[1:]
    ## 取出新增一行從第二個開始所有單元格
    row_cells = t.add_row().cells[1:]
    for header_cell, row_cell in zip(header_cells, row_cells):
        k = int(header_cell.text[:4].replace('ʰ', '9'))
        symbol_dict = symbols[k]
        if not pd.isna(symbol_dict['詞2字']):
            row_cell.add_paragraph()
        for i, p in enumerate(row_cell.paragraphs, 1):
            p.style.font.name = 'IPAPANNEW'
            p.add_run(symbol_dict[f'詞{i}字']).font.name = "宋體"
            for n, e in enumerate(symbol_dict[f'詞{i}音']):
                run = p.add_run(e)
                if n % 2 == 1:
                    run.font.superscript = True

doc.save("result.docx")
           

複制

最終結果:

利用python實作字音回填