东莞市盛裕绒艺玩具有限公司

东莞市盛裕绒艺玩具有限公司

易利easybet备用客户端

13562483966
联系方式
全国服务热线: 13562483966

咨询热线:15140778148
联系人:程建功
地址:宁夏 银川 市市郊区银新乡开发区6号楼

VSTO中Word的查找方式

来源:易利easybet备用客户端   发布时间:2019-11-20   点击量:104

VSTO中Word的查找方式

前言

使用C#在VSTO开发Word插件的过程,经常需要对文档中的内容进行查找和替换。在Word中进行文本的查找替换,和一般对纯文本的查找替换却不太一样。因为Word文档是一个富文本对象,对文本的查找实际上是对一个对象的查找,而这个或者这种对象对于开发者是未知不可见的,因此和纯文本搜索比较,不仅存在许多不一样的地方,也存在一定的难度。本文主要对这些差异进行了讨论和分析。

正则全文搜索

通常情况下,对一个文本进行查找,我们会使用正则表达式,找到匹配模式的位置,如下所示。

var pattern = "[0-9]+?"; var content = "第1个"; var mc = System.Text.RegularExpressions.Regex.Matches(content, pattern); if (mc.Count > 0) { foreach (System.Text.RegularExpressions.Match m in mc) { //获取匹配字符串在输入中的索引位置 int searchIndex = m.Index; } }

而在Word文档中,我们可以通过range.Text属性获取到文档的字符串表示,利用相同的正则表达式来进行查找。

var pattern = "[0-9]+?"; //获取文档的全部字符 var content = doc.Range().Text; var mc = System.Text.RegularExpressions.Regex.Matches(content, pattern); if (mc.Count > 0) { foreach (System.Text.RegularExpressions.Match m in mc) { //获取匹配字符串在输入中的索引位置 int searchIndex = m.Index; } }

如果这个文档都是由纯文本组成的,那么得到的位置,就是查找字符在全文中的真实位置。然而实际上大多数情况下,文档还可能有图片、表格、公式和图表等格式组成,不仅仅是文本组成。

range位置的替换

Word在将文档转换成Text的过程中,如果格式是文本,那么一个字符A就将转换成一个字符A(复制);如果格式是富文本对象,比如图片,那个一个图片将会转换成一个空字符串“ ”,仅表示位置。然而,在Word文档中,纯字符串类型的range的长度就是字符串的长度;非字符串对象的range的长度不确定。所以,经过Text的转换后,range的位置信息缺少了。查找到字符串在Text的位置,并不能找到该字符串在全文的range位置,也就无法对查找的字符串进行操作了。

//*表示任意字符,range长度为1//A表示一个图片,range长度为4//B表示一个公式,range长度为6//Word文档中全文的range位置****A**B**(1)(2)(3)(4)(8)(9)(10)(17)(18)(19)//Text的位置, 如图**** ** **(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)

既然字符串的range位置丢失了,索性事先把所有对象的位置先存储起来。

/// <summary>/// 从range中获取每一个字符的实际位置/// </summary>/// <param name="range">选中部分</param>/// <returns>位置列表</returns>public List<int> GetRangeLocation(Word.Range range){ var ret = new List<int> { }; foreach (Word.Range c in range.Characters) { ret.Add(c.Start); } return ret;}

利用位置信息,终于可以得到一个可以正确查找文本的方法了。

/// <summary>/// 根据模式,找到所有匹配的位置/// </summary>/// <param name="range">选中部分</param>/// <param name="pattern">模式</param>/// <returns>匹配列表</returns>public List<Word.Range> SearchRangeInPattern(Word.Range range, string pattern){ var ret = new List<Word.Range> { }; var content = range.Text; var doc = range.Document; //获取实际的字符位置 var locationList = GetRangeLocation(range); var mc = System.Text.RegularExpressions.Regex.Matches(content, pattern); if (mc.Count > 0) { foreach (System.Text.RegularExpressions.Match m in mc) { var searchStart = m.Index; var searchEnd = m.Index + m.Value.Length; //将text位置转换为range位置 var realStart = locationList[searchStart]; var realEnd = locationList[searchEnd]; //获取匹配的range位置 var itemRange = doc.Range(realStart, realEnd); ret.Add(itemRange); } } return ret;}

实际运用

在实际运用的过程中,基本不能采用这种全文的正则查找方式,除非要搜索的文本内容长度很小。因为经过测试,获取字符的实际range位置,具有非常大的时间开销。原因在于获取每一个字符都是一次COM调用,调用时间数量级在10毫秒左右。多次的COM调用,使得总调用时间非常大。

find和replace的API查找

在Word的API存在定义好的查找函数,可以使用Word定义的规则(类似于正则表达式)的方式,进行通配符查找。

/// <summary>/// 替换选中部分的文字/// </summary>/// <param name="range">选中部分</param>/// <param name="search">待替换文字</param>/// <param name="replace">替换文字</param>public static void SearchReplace(Word.Range range, string search, string replace){ range.Find.ClearFormatting(); range.Find.Text = search; //使用通配符搜索 range.Find.MatchWildcards = true; range.Find.Replacement.ClearFormatting(); range.Find.Replacement.Text = replace; object replaceAll = Word.WdReplace.wdReplaceAll; object missing = Type.Missing; range.Find.Execute(ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref replaceAll, ref missing, ref missing, ref missing, ref missing);}

使用这种方法,查找速度快,执行时间短。但是缺点也很明显,匹配经常不准确,比如空格和换行符,由于图片的悬浮位置影响,无法匹配。此外基于通配符的匹配,毕竟不是正则表达式,不支持零字符位匹配和or匹配,所以用处有限,许多功能无法实现。

\捕获0-无限个数字,[0-9]* //正则表达式,若干个数字,包括0个[0-9]{1,} //Word,若干个数字,必须1个以上\捕获数字或者字母[0-9]|[a-z] //正则表达式,一个数字或一个字母[0-9] then [a-z] //word,只能分成两次来匹配,不支持or的匹配

总结对比

方式查找速度匹配准确度匹配模式
正则全文搜索非常慢, 多次COM调用正则表达式,类型多,只支持文本
Find查找快,一次COM调用通配符,类型少,支持多种对象查找

相关产品

COPYRIGHTS©2017 易利easybet备用客户端 ALL RIGHTS RESERVED 备案号:104