VSTO中Word的查找方式
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调用 | 中 | 通配符,类型少,支持多种对象查找 |
VSTO中Word的查找方式的更多相关文章
- VSTO中Word的Range复制方式
VSTO中Word的Range复制方式 前言 VSTO是一套用于创建自定义Office应用程序的Visual Studio工具包,通过Interop提供的增强Office对象,可以对Word文档进行编 ...
- VSTO中Word转换Range为Image的方法
VSTO中Word转换Range为Image的方法 前言 VSTO是一套用于创建自定义Office应用程序的Visual Studio工具包,通过Interop提供的增强Office对象,可以对Wor ...
- <转>Python中的新式/经典类的查找方式
在学习到深度和广度的时候,懵了很久.后来看到这篇文章,恍然大悟.写的很好.特意转过来. 经典类: 只要有父类, 就会沿着一直找, 即使已经找过了~ 新式类: 在类继承的多个类拥有共同父类的情况下, 会 ...
- ExtJS 4.2 组件的查找方式
组件创建了,就有方法找到这些组件.在DOM.Jquery都有各自的方法查找元素/组件,ExtJS也有自己独特的方式查找组件.元素.本次从全局查找.容器内查找.form表单查找.通用组件等4个方面介绍组 ...
- linux中5条查找命令
1 which which命令的作用是,在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果. which [文件...] 参 数: -n<文件名长度> 指定文件名长 ...
- (转)javascript中的对象查找
本文转自:http://otakustay.com/object-lookup-in-javascript/ ---很棒的一篇文章,作者的其他文章还暂时没读,但相信作者是一个谦虚 谨慎的好工程师 近 ...
- c++ --> c++中四种类型转换方式
c++中四种类型转换方式 c风格转换的格式很简单(TYPE)EXPRESSION,但是c风格的类型转换有不少缺点, 1)它可以在任意类型之间转换,比如你可以把一个指向const对象的指针转换成指向 ...
- 在 Vim 中优雅地查找和替换(转)
总有人问我 Vim 中能不能查找,当然能!而且是超级强的查找! 这篇文章来详细介绍 Vim 中查找相关的设置和使用方法. 包括查找与替换.查找光标所在词.高亮前景/背景色.切换高亮状态.大小写敏感查找 ...
- Linux中的文件查找技巧
前言 Linux常用命令中,有些命令可以帮助我们查找二进制文件,帮助手册或源文件的位置,也有的命令可以帮助我们查找磁盘上的任意文件,今天我们就来看看这些命令如何使用. witch witch命令会在P ...
随机推荐
- flink入门实例-Windows下本地模式跑SocketWordCount
一般情况下,开发大数据处理程序,我们希望能够在本地编写代码并调试通过,能够在本地进行数据测试,然后在生产环境去跑“大”数据. 一.nc工具 配置windows的nc端口,在网上下载nc.exe(htt ...
- [转载]SSH框架搭建详细图文教程
http://www.cnblogs.com/hoobey/p/5512924.html
- electron 打包后node_modules 体积过于庞大
问题产生原因:过多无用的模块被打包放入到程序中 解决: 删除 package-lock.json ,是由于这个文件导致打包时候放入过多文件,删除即可.
- DevExpress XtraTabbedMdiManager删除Page
DevExpress XtraTabbedMdiManager删除Page 时,xtraTabbedMdiManager1.Pages.Remove()是没用的. 正确的应该是xtraTabbedMd ...
- TCSL:遇到网络正常,但是添加网口打印机总是失效的问题。
1. 环境 这家店要换成TCSL餐饮系统,但是店主希望在换系统时候,保持原来系统正常运转.所以,一开始踩点和实施都是小心翼翼~~ 不过,还是遇到问题,没法打印,如果开启TCSL打印服务,就会和原来的餐 ...
- UEditor之实现配置简单的图片上传示例
UEditor之实现配置简单的图片上传示例 原创 2016年06月11日 18:27:31 开心一笑 下班后,阿华到楼下小超市买毛巾,刚买完出来,就遇到同一办公楼里另一家公司的阿菲,之前与她远远的有过 ...
- PTA第二次作业
pta 6-7题 删除字符串中数字字符 1.设计思路 (1)第一步:观察题意了解各个参数与所需函数在题目中的意义: 第二步:设计算法编写函数,让函数的功能实现题目中所需的功能: 第三步:运行程序检测是 ...
- Puppeteer 应用容器化
Puppeteer 应用容器化 Intro Puppeteer是谷歌官方出品的一个通过DevTools协议控制headless Chrome的Node库.可以通过Puppeteer的提供的api直接控 ...
- VSCode调试Flutter的问题解决
错误:Target of URI doesn't exist: package:fultter/material.dart 原因:flutter版本不匹配.flutter升级到了最新版本,而项目引用的 ...
- 常见的java设计模式
单例模式 简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()的方法来获取它们的实例. getInstan ...