阅读目录

在各类应用系统开发中,和Word相关的应用可谓相当广泛。如各类MIS系统、各种和实际业务结合紧密的系统、需要制式报表的系统等,都需要对Word进行操作,典型的应用包括:

1、内嵌Word。在系统中内嵌Word,这样,既可以利用Word强大的功能进行文档的新建、编辑、修改、排版,同时还节省了用户对于编辑器操作的学习成本,提高了文档格式的通用性。

2、Word的二次开发。通过Word自带的宏,利用VBA(Visual Basic Appplication)进行开发,实现各种复杂的自动化功能。

3、前台不显示Word操作界面,而在后台对Word文档进行操作。包括:1)读入word文档,解析内容,获取需要的数据;2)把数据写入Word模板,生成符合格式要求的Word文档。

上述应用中,前两个应用领域相对特定,且需要对Word进行深度的二次开发,本人涉猎有限,因而不进行过多的讨论。而对于第三种应用,由于Word软件的普及率非常高,基本上可以把DOC文档看作是一个通用的文档结构。同时,Word在格式控制方面功能非常强大。因此,使用Word来制作输出文件或者报表,不光格式易于控制(用户可以在Word中制作好需要的模板,替换真实数据就获得需要的输出文档或者报表),用户的接受度等方面都有很大的优势,近年来越来越受到重视。下文主要尝试讨论如何利用Word模板生成需要的Word文档的实现。

Word二次开发概况

1983年,微软发布了基于MS-DOS的Word 1.0版,至今已经30余年了。对于Word的二次开发,也是有着悠久的历史。就本人的开发经验而言,在近十年前,就已经在Visual Basic 6.0平台上,进行内嵌Word的开发,这个在当年也是非常流行的一种开发。时至今日,Word的二次开发仍然是每个开发者频繁遇到的问题。

但是,Word的开发相对于其他的二次开发,甚至于相对于同门的也很复杂的Excel来说,开发的难度都要大很多,原因来自以下方面:

1、Word 的对象结构复杂。由于Wrod有着久远的历史,这既是它的优势也是它的包袱,它必须要保持足够的兼容性,因此DOC文档结构也就变得非常的复杂了。在Word中,有着复杂的对象结构,如Application、Document、BookMarks、Range、Selection、Paragraph等,它们之间既有层级关系,还有嵌套关系,有时为了一个小小的功能,却无法找到操作的对象。

Word 的对象结构

2、Word功能复杂。作为微软的拳头产品,多年以来,Word的功能越来越强大。尽管大多数的功能对于二次开发来说是完全用不到的,但还得去了解和学习,这就需要付出额外的代价。以Find为例,其参数竟然高达15个,如下所示:

Find.Execute(FindText, MatchCase, MatchWholeWord, MatchWildcards, MatchSoundsLike, MatchAllWordForms, Forward, Wrap, Format, ReplaceWith, Replace, MatchKashida, MatchDiacritics, MatchAlefHamza, MatchControl)

但大多数情况下,我们只会用到FindText、ReplaceWith等极少数参数而已。

3、版本问题。Word的众多版本也给二次开发带来很多困扰,开发者必须要对于当前多种Word版本都存在的情况有所考虑,并做好兼容性的处理才行。

使用DsoFramer进行开发

谈到Word的二次开发,就必须要提到DsoFramer。它是微软提供一款开源的用于在线编辑、调用Word、 Excel 、PowerPoint等的ActiveX控件。国内很多著名的OA中间件,电子印章,签名留痕等大多数是依此改进而来的。

DsoFramer操作Word很简单,加载ActiveX控件后就可以直接操作Office文档了。以我们要进行的主要操作——替换文档中的关键字为例,在Visual Basic中代码如下:

dso.Save "c:\new2.doc" dso.Close

在VB6中加载控件,如下图所示:

由于DsoFramer是COM时代的产物,适用于VB、VC开发者,在 .Net下开发,或者进行Web应用开发,就显得有点力不从心。在实际开发中,常常出现一些莫名其妙的错误。另外,它的工作模式需要先在界面中打开文档再进行各种操作,这种模式也不适应Web应用程序的需要。

使用Interop进行开发

微软在.Net框架下,推出了Microsoft.Office.Interop.Word及其他的互操作方式,能够更好地对Office文档进行二次开发。

使用Interop进行二次开发,首先需要了解Word的对象结构,完整的Word对象结构图如下(来自官方的VBA_Word帮助文件):

Application: 用来表现WORD应用程序,包含其它所有对象。他的成员经常应用于整个Word,可以用它的属性和方法控制Word环境。

Document对象: Document对象是Word编程的核心。当打开一个已有的文档或创建一个新的文档时,就创建了一个新的Document对象,新创建的Document将会被添加到Word Documents Collection。

Selection: Selection对象是描述当前选中的区域。若选择区域为空,则认为是当前光标处。

Rang: 是Document的连续部分,根据起始字符的结束字符定议位置。

Bookmark: 类似于Rang,但Bookmark可以有名字并在保存Document时Bookmark也被保存。

打开关闭和写入操作

了解到Word的对象结构后,就可以考虑怎样操作了。

1、如何打开和关闭Application及Document对象。

打开和关闭操作比较简单,实现代码如下:

//打开 Microsoft.Office.Interop.Word.Application app = new Microsoft.Office.Interop.Word.Application(); Microsoft.Office.Interop.Word.Document doc = app.Documents.Open(ref fn, ref oMiss, ref oTrue, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oTrue,ref oMiss, ref oMiss, ref oMiss, ref oMiss); //关闭 doc.Close(ref oFalse, ref oMiss, ref oMiss); doc = null; app.Quit(ref oFalse, ref oMiss, ref oMiss); app = null;

2、写入

由于Word的结构复杂,要找到写入的位置就比较复杂。在Interop操作中,可以对Range的text进行操作,如:

doc.Range.Text="newtext";

批量替换文本

写入报表,最常用的方法,是把模板做好,定义好特征串,进行替换即可。自然而然我们想到了通过Word的替换功能来完成。其主要代码如下:

object s1 = OldString; object s2 = NewString; object rep = Microsoft.Office.Interop.Word.WdReplace.wdReplaceAll; doc.Content.Find.ClearFormatting(); doc.Content.Find.Execute( ref s1, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref oMiss, ref s2, ref rep, ref oMiss, ref oMiss, ref oMiss, ref oMiss);

用简单的字符串测试,代码工作正常,但是,用实际的数据测试发现无法完成替换。追踪后发现问题:替换的目标字符串不能过长,否则就会替换失败,这个结果和Word软件中替换的实际情况一致。

遍历段落替换文本

由于批量查找替换操作不能完成替换成长文本目标,直观的解决思路就是采用手动的方式,找到一个特征串替换一个。但是在Interop中,由于Find对象比较复杂,多次尝试没有成功,比较实验后,发现可以采用遍历方式进行替换。

由于文档下有多个段落,因而可以对文档中的每个段落进行遍历,如果在段落中找到特征串,就把段落的文字提取出来,放在字符串中,对该字符串进行替换后再重新赋值给这个段落。这种方式需要段落的格式保持一致,这样就可以拼出完成段落来了。核心代码如下:

) { doc.Paragraphs[i].Range.Text = doc.Paragraphs[i].Range.Text.Replace(OldStringArray[k], NewStringArray[k]); } } catch { } }

在实际操作中,发现遍历操作非常容易出错,原因在于文档对象存在着很多的段落,超过了可以看见的段落数量,因此就必须加入一个错误捕获功能以忽略一些意外的错误。

通过这种替换,可以成功的完成整段的替换,效果如下图:

如果被替换的特征串并不是独立的段落、或者位于表格中的话,上述代码能否工作正常呢?如下图所示,在段落中和表格中增加两个特征串进行替换,结果如下图所示:

结果可以看到,表格中虽然顺利替换,但格式还是受到影响。而段落中的文字虽然替换了,格式也被改为统一的格式了。

查找后逐个替换文本

对于一个追求完美的程序员来说,上述的bug是无法容忍的,尽管它已经可以凑合使用了,但要忽视的确做不到。根据前面的铺垫,可能感觉到问题的解决还得把Word的内部构造搞清楚。

在网上搜索了很久,都没办法找到关于查找和替换的更详细的解决方法。经过一段时间的困惑之后,突然发现,其实这些资料我自己本身就有。就是使用VBA开发Office的一系列资料,里面关于Word的对象结构,有着远比网上只言片语靠谱的解答。学习的过程直接跳过,把几条重要的结论给出来:

1)用Content的Find查找,只能进行批量的查找和替换,如果想找到第一个,停下来,操作,是不行的。

2)上述的“查找——操作”的思路,只能用Selection对象来完成,而Selection对象,Document的属性中没有、Content的属性中也没有。只有谁有?Application!

3)用Application的Selection的Find找到后,结果就在Selection.Text中,但要替换,只能对Selection.Range.Text进行赋值才行。

下面是实现代码:

); app.Selection.Find.Execute(ref oFindText,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oTrue,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oMiss,ref oMiss); if (app.Selection.Find.Found) { app.Selection.Range.Text=NewString; }

再次对上述第二种模板进行替换,结果如下:

这段来之不易的代码,当然要保存在CommonCode(v2.0.6)中,以后要调用Word模板实现生成新文档就非常简单了,代码如下:

CommonCode.WordUtil.ReplaceAndSave(Application.StartupPath + "\\temp2.doc", Application.StartupPath + "\\1.doc", new string[] { "[%单选%]", "[%分数%]", "[%数量%]" }, new string[]{@"
1、关于公开信息搜密,正确的是

A.在互联网公开信息中搜密需要高深的技术

B.在互联网中的主流网站中不存在秘密

C.只要通过关键词搜索和定期跟踪网站就可能找到秘密信息

D.公开信息搜密因为方法简单,所以效果较差,不受重视","98","10"});

结论

对于替换Word模板内容生成Word文档的需求,在.Net下可以采用Interop的方式来实现。具体的实现手段,有批量替换、遍历替换、单步查找并替换等方式。批量替换不能进行长文本的替换故不可用,遍历段落替换不能对段内的关键词进行保持格式的替换,也不完美。单步查找替换调用全局的查找功能(app.Selection.Find),并能够定位查找到的内容并进行操作,是完成需求的最佳方案。

单步查找替换实现方案被整合至CommonCode.WordUtil.ReplaceAndSave函数中,可以直接使用。

Demo下载

说明:引用CommonCode.dll和Microsoft.Office.Interop.Word.dll即可。

原来demo缺了log4net引用,添加

demo-2

【3】利用Word模板生成文档的总结的更多相关文章

  1. nodejs根据word模板生成文档(方法二)

    [推荐该方法,模板比较简洁] 1,代码, 这里采用的模块为 docxtemplater 和 open-docxtemplater-image-module,均为开源(docxtemplater 有收费 ...

  2. nodejs根据word模板生成文档

    这里使用的模块是 docx-templates,直接npm安装即可[方法二:https://www.cnblogs.com/vichang/p/10416449.html]  1,模板文件 +++QU ...

  3. doc2vec 利用gensim 生成文档向量

    利用gensim 直接生成文档向量 def gen_d2v_corpus(self, lines): with open("./data/ques2_result.txt", &q ...

  4. StarUML配置Word生成文档模板

    来源:fasiondog 许多UML建模工具可以自动生成文档,让需求人员.开发人员专心于需求.设计的建模.当然为了能够生成符合自己要求的模板,需对建模时的目录结构(模型和包)有所规划和要求,否则很难生 ...

  5. C#依据word模版动态生成文档

    新生开学,各院系辅导员代领校园卡.需要打印一份领取卡的协议,协议模版固定,但各院系卡的数量不同.需要从excel表格中抽取数据往word文件中填,同事咨询是否可以用word中的邮件合并功能,心想有这功 ...

  6. 利用PowerDesigner逆向工程导出PDM模型及生成文档

    原文:利用PowerDesigner逆向工程导出PDM模型及生成文档 最近需要对老项目进行重构优化,由于项目都是好几年前的,相关设计资料很不全,最基本的数据库设计文档都没有,只能利用PowerDesi ...

  7. PhpDocumentor 生成文档

    最近项目需要phpdoc生成文档,首先安装PhpDocumentor,利用pear安装: 切换用户: su root 安装PhpDocumentor: pear install PhpDocument ...

  8. C# 利用WORD模板和标签(bookmark) 批量生成WORD

    前言: 由于对C#操作WORD不熟悉,也就留下这么一篇水文,别吐糟...=_=||| 利用Microsoft.Office.Interop.Word (2003版也就11版)——因为部分客户端还是用O ...

  9. 通过EA导入数据库存在表结构并生成文档

    通过EA导入数据库存在表结构并生成文档   慕课网,程序员升职加薪神器,点击免费学习 目录[-] 导入数据源,表结构 生成表结构的文档 Enterprise Architect 是超级强大项目管理功能 ...

随机推荐

  1. Bzoj2510 弱题(矩阵快速幂)

    题面(权限题) 题解 一道概率\(dp\),可以设\(f[i][j]\)表示第\(i\)次操作后,标号为\(j\)的小球的期望个数,那么有: \[ \begin{aligned} &f[i][ ...

  2. WP SyntaxHighlighter 初探

    继上篇文章发布后,我随即去网上找了下博客园.CSDN他们用的高亮工具,果然都是用的别人的,SyntaxHighlighter.去官网上看了下,很强大,包含各种经典配色以及多语言.要想在wordpres ...

  3. POJ 3228 [并查集]

    题目链接:[http://poj.org/problem?id=3228] 题意:给出n个村庄,每个村庄有金矿和仓库,然后给出m条边连接着这个村子.问题是把所有的金矿都移动到仓库里所要经过的路径的最大 ...

  4. 「Luogu4321」随机游走

    「Luogu4321」随机游走 题目描述 有一张 \(n\) 个点 \(m\) 条边的无向图,\(Q\) 组询问,每次询问给出一个出发点和一个点集 \(S\) ,求从出发点出发随机游走走遍这个点集的期 ...

  5. 按考分对学生排序 Exercise08_03

    /** * @author 冰樱梦 * 时间:2018年12月 * 题目:按考分对学生排序 * */ public class Exercise08_03 { public static void m ...

  6. Android消息机制——Handler

      /**android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个MessageQueue(消息队列), * 但是MessageQueue被封装到Looper里 ...

  7. 2015 UESTC 搜索专题J题 全都是秋实大哥 kmp

    全都是秋实大哥 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/61 Desc ...

  8. Codeforces Round #301 (Div. 2) D. Bad Luck Island 概率DP

    D. Bad Luck Island Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/540/pr ...

  9. Eclipse打开.class文件

    1.准备材料 jad.exe    下载地址  https://varaneckas.com/jad/,根据系统去选择(就当是废话)  下载下来的是jad158g.win.zip压缩文件,解压后有个j ...

  10. mysql server has gone away的原因

    之前遇到开发询问“mysql server has gone away”的问题,想当然的就认为是由于太长时间没有操作,导致超过MySQL服务端上的wait_timeout的设置,最终连接被MySQL服 ...