正在开发人事管理系统的入职登记表 word 导出功能,使用 golang 的 “github.com/nguyenthenguyen/docx” 包进行 word 模板替换时(参考前文: golang 对 word docx 文档中的占位符进行替换),遇到了一个诡异的问题。
就是大部分占位符都能正确替换,但有几个占位符却无法替换成功。
我开始以为是代码的问题,检查了半天没有发现问题。
然后,我又怀疑是这个三分库的 bug,可能出了异常,但是没有把 error 返回出来。
但是,感觉也不太可能,毕竟在类似的使用场景下,大部分占位符都能正确替换,说明这个库的基本功能是正常的。
最后,我才想到,可能是 word 模板本身的问题。
问了一下 DeepSeek,也印证了这种猜测。
文本被拆分
在 DOCX 的 XML 中,一个段落(
<w:p>
<w:r><w:t>你</w:t></w:r>
<w:r><w:t>好</w:t></w:r>
</w:p>
此时,golang 代码就没法正确替换
doc.Replace("你好", "Hello", -1)
排查方法
怎么能看到 word 文件的 XML 结构呢?其实很简单,DOCX 文件本质上是一个 ZIP 压缩包,我们可以直接解压它来查看内部的 XML 文件。
- 将 .docx 文件的后缀改为 .zip。
- 直接右键解压缩这个 ZIP 文件。
可以看看这个文件夹的目录结构:
> tree
.
├── [Content_Types].xml
├── _rels
├── customXml
│ ├── _rels
│ │ └── item1.xml.rels
│ ├── item1.xml
│ └── itemProps1.xml
├── docProps
│ ├── app.xml
│ ├── core.xml
│ └── custom.xml
└── word
├── _rels
│ ├── document.xml.rels
│ └── header1.xml.rels
├── document.xml
├── endnotes.xml
├── fontTable.xml
├── footnotes.xml
├── header1.xml
├── media
│ └── image1.png
├── settings.xml
├── styles.xml
└── theme
└── theme1.xml
8 directories, 18 files
这个 work 文件的结构真是非常值得学习,看起来就像是一个小型网站模板系统一样,图片和样式都被分离出来了,文档内容在 document.xml 中,样式在 styles.xml 中,图片在 media 文件夹中。
最后再用 zip 打包,只是后缀改成了 docx,真是天才。
Excel 也是类似的,特别是在大数据量下,用了 ZIP 压缩可以大大减少文件大小,相比 csv 文件来说,docx 和 xlsx 的文件大小可以小很多。
如果 Markdown 搞个扩展模式,例如 mdx, 也可以通过类似的方式把图片整合进去,感觉也很棒。
言归正传,我们打开 document.xml 文件,搜索一下正常的占位符文本(例如, 占位符 GT1 可以被正确的替换),会看到确实是没有分割:
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia" w:ascii="宋体" w:hAnsi="宋体" w:cs="宋体"/>
<w:bCs/>
<w:sz w:val="21"/>
<w:szCs w:val="21"/>
</w:rPr>
<w:t>GT1</w:t>
</w:r>
而异常的占位符(例如, 占位符 GT2)被分割成了两部分:

<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>GT</w:t>
</w:r>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:lang w:val="en-US" w:eastAsia="zh-CN"/>
</w:rPr>
<w:t>2</w:t>
</w:r>
这就是为什么 GT1 能被正确替换,而 GT2 却无法替换的原因了。
解决方法
最简单直接的方法,就是删掉有问题的占位符,然后手动敲一遍。
⚠:千万不要尝试从别处复制黏贴字符串过来,然后修改某个字符。word 的复制黏贴会产生意想不到的格式变化,即便你复制黏贴过来清除格式,再应用格式也不行。一定要手动敲一遍。
我现在才理解为何发票之类的文件,默认都是采用 PDF 或者图片了。Word 的格式调整能浪费你一天时间。
Latex 会不会是更好的选择呢?
Word 虽然编辑起来方便,但是遇到类似的占位符替换问题,排查起来真的非常麻烦。
不知道 Latex 会不会是更好的选择呢?毕竟它是基于文本的,直接替换字符串就行了,不会有被拆分成多个 run 的问题。
关于作者 🌱
我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式