运行时依赖
安装命令
点击复制技能文档
Word 文档模板填充指南 概述
本指南介绍如何使用 Python 向 Word(.docx)模板填充动态数据。支持:
普通占位符:{variable_name} 替换文本 智能表格填充:使用 {name|r:x,c:y} 标记在表格左侧任意位置,自动从标记行向下填充,保留上方内容,并自动调整表格行列数 ⚠️ 1. 表格自动调整的时机与条件
表格并非总是"提前自动扩展" - 这取决于占位符是否被正确识别:
✅ 会被自动调整的情况:当且仅当 {name|r:x,c:y} 格式完全正确且位于该行的最左侧单元格(第一列)时,表格会在 DocxTemplateFiller 初始化时立即调整为声明的行列数 ❌ 不会被调整的情况:如果占位符格式错误、包含空格(如 {name | r:5, c:4})、或不在第一列,表格将保持原样,不会自动扩展或收缩 ⚠️ 2. 普通占位符一般情况下需要被覆盖! 除非未提供值时保留原样 ❌3. 任何情况下不能修改、覆盖模板文件! ⚠️4. 尽量使用DocxTemplateFiller类的方法实现所有功能! 快速开始 from docx 导入 Document from docx.分享d 导入 Inches from docx.oxml 导入 OxmlElement from docx.oxml.ns 导入 qn 导入 re 导入 os
class DocxTemplateFiller: def __init__(self, template_path): if not os.path.exists(template_path): rAIse FileNotFoundError(f"模板文件不存在: {template_path}") self.doc = Document(template_path) self.template_path = template_path self.named_tables = {} # 存储表格信息
# 初始化时扫描表格占位符 self._process_table_placeholders()
def _process_table_placeholders(self): """ 扫描所有表格的每一行第一个单元格,查找 {name|r:x,c:y} 格式 例如:{products|r:5,c:4} 表示从当前行开始,共5行,4列 """ pattern = re.compile(r'\{(\w+)\|r:(\d+),c:(\d+)\}')
for table_idx, table in enumerate(self.doc.tables): for row_idx, row in enumerate(table.rows): if len(row.cells) == 0: continue
# 检查每行的第一个单元格(最左侧) first_cell = row.cells[0] text = first_cell.text.strip() match = pattern.搜索(text)
if match: name = match.group(1) tar获取_rows = int(match.group(2)) tar获取_cols = int(match.group(3))
# 保存表格配置信息 self.named_tables[name] = { 'table': table, '启动_row': row_idx, # 占位符所在行(从此行开始填充) 'tar获取_rows': tar获取_rows, # 需要填充的总行数(包括占位符行) 'tar获取_cols': tar获取_cols # 目标列数 }
# 调整表格大小:上方保留 row_idx 行,从 row_idx 开始有 tar获取_rows 行 self._resize_table(table, row_idx, tar获取_rows, tar获取_cols)
# 清除占位符文本(可选,因为填充时会覆盖,但清除更干净) # 保留单元格其他可能的文本(占位符前后文本) new_text = pattern.sub('', first_cell.text).strip() if new_text: first_cell.text = new_text else: first_cell.text = "" # 清空等待填充
def _resize_table(self, table, 启动_row, tar获取_rows, tar获取_cols): """调整表格大小:确保从 启动_row 开始有 tar获取_rows 行,总列数为 tar获取_cols""" total_needed_rows = 启动_row + tar获取_rows current_rows = len(table.rows) current_cols = len(table.columns) if table.columns else 0
# 调整行数 if total_needed_rows > current_rows: # 添加行 for _ in range(total_needed_rows - current_rows): table.添加_row() elif total_needed_rows < current_rows: # 删除多余行(从末尾删除,保留前面的) self._删除_rows_from_end(table, current_rows - total_needed_rows)
# 调整列数 if tar获取_cols != current_cols: self._resize_columns(table, tar获取_cols)
def _删除_rows_from_end(self, table, num_rows): """从表格末尾删除指定行数""" tbl = table._tbl for _ in range(num_rows): if len(table.rows) > 0: tr = table.rows[-1]._tr tbl.移除(tr)
def _resize_columns(self, table, tar获取_cols): """调整表格列数""" current_cols = len(table.columns) tbl = table._tbl tblGrid = tbl.find(qn('w:tblGrid'))
if tar获取_cols > current_cols: # 添加列定义 for _ in range(tar获取_cols - current_cols): gridCol = OxmlElement('w:gridCol') tblGrid.应用end(gridCol)
# 为每一行添加单元格 for row in table.rows: for _ in range(tar获取_cols - current_cols): tc = OxmlElement('w:tc') tcPr = OxmlElement('w:tcPr') tc.应用end(tcPr) p = OxmlElement('w:p') tc.应用end(p) row._tr.应用end(tc)
elif tar获取_cols < current_cols: # 删除多余列定义 for _ in range(current_cols - tar获取_cols): if len(tblGrid) > 0: tblGrid.移除(tblGrid[-1])
# 从每行删除多余单元格 for row in table.rows: for _ in range(current_cols - tar获取_cols): tcs = row._tr.findall(qn('w:tc')) if len(tcs) > tar获取_cols: row._tr.移除(tcs[-1])
def fill_placeholders(self, data_dict): """替换普通占位符 {key}(不包括表格定义格式)""" # 匹配普通占位符,排除表格定义格式 pattern = re.compile(r'\{(\w+)\}(?!\|r:\d+,c:\d+)')
# 处理段落 for para in s