ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 34058|回复: 53

[原创] WordVBA中字符串查找替换进阶

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2016-11-9 19:58 | 显示全部楼层 |阅读模式
本帖最后由 duquancai 于 2016-11-10 20:43 编辑

一直以来,大家都熟悉WordVBA中可以创建正则对象,利用强大的正则表达式来处理字符,但是大家都熟悉Word文档都是含有诸多的格式、样式以及表格等,虽然这个正则对象是处理字符串之王,但是苦于只能处理“纯文本”,大家既爱又恨!
鉴于以上情况,本帖就是要解决WordVBA中大家既爱又恨正则对象,本帖解决在Word文档含有诸多的格式、样式以及表格等而不改变这些格式、样式及表格等前提下在WordVBA中创建正则来解决两大类型问题:1、修改格式,2、查找并替换字符
前条件:在Word文档含有诸多的格式、样式以及表格等,以下代码在不对原文档做修改下进行。
        1、修改格式
  1. Sub Word文档中修改格式()
  2.     Dim i As Paragraph, mt, oRang As Range, n%, m%
  3.     With CreateObject("vbscript.regexp")
  4.         .Pattern = "^第[^条]+条" '这里各显其能输入正则表达式(根据自己的需求)
  5.         .Global = True: .IgnoreCase = False: .MultiLine = True
  6.         For Each i In ActiveDocument.Paragraphs
  7.             For Each mt In .Execute(i.Range.Text)
  8.                 m = mt.FirstIndex: n = mt.Length
  9.                 Set oRang = ActiveDocument.Range(i.Range.Start + m, i.Range.Start + m + n)
  10.                 oRang.Bold = True '字符加粗,有一系列的修改格式(根据自己的需求)
  11.             Next
  12.         Next
  13.     End With
  14. End Sub
复制代码
        2、查找替换
  1. Sub Word文档中查找替换()
  2.     Dim col As New Collection, k&
  3.     Dim i%, mt, oRang As Range, n%, m%
  4.     With CreateObject("vbscript.regexp")
  5.         .Pattern = "" '这里各显其能输入正则表达式(根据自己的需求)
  6.         .Global = True: .IgnoreCase = False: .MultiLine = True
  7.         For Each i In ActiveDocument.Paragraphs
  8.             For Each mt In .Execute(i.Range.Text)
  9.                 k = k + 1
  10.                 m = mt.FirstIndex: n = mt.Length
  11.                 Set oRang = ActiveDocument.Range(i.Range.Start + m, i.Range.Start + m + n)
  12.                 col.Add oRang, CStr(k)
  13.             Next
  14.         Next
  15.     End With
  16.     For j = 1 To col.Count
  17.         col(j) = "" '替换为字符(根据自己需要)
  18.     Next
  19. End Sub
复制代码
注:关于正则表达式的语法不在本帖讨论范围!!!

评分

12

查看全部评分

TA的精华主题

TA的得分主题

发表于 2016-11-10 18:11 | 显示全部楼层
好帖子,要是考虑更多word自带情况,比如替换位置如果有浮动对象是否替换会有问题等,就更好了。

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-11-10 19:05 | 显示全部楼层
本帖最后由 duquancai 于 2016-11-10 19:21 编辑
cuteword 发表于 2016-11-10 18:11
好帖子,要是考虑更多word自带情况,比如替换位置如果有浮动对象是否替换会有问题等,就更好了。

感谢龚老师的提醒!我测试了一下:Word文档中有表格、有字符格式、有自选图形(浮动式自选图形)、有嵌入式图片、有浮动式图片等,替换位置有这些对象,查找替换也不会有影响。
原因:第一个for each循环是按照遍历段落,在所有的段落中查找得到多个range对象并重新定义的range对象变量,并把查找到的所有的range对象按照循序储存到一个集合;第二个for   j循环是逐个从集合中取出之前查找到的range对象并逐个替换查找到的range对象。不会有任何的影响啊?
顺便更正以下:这句定义变量 Dim i%, mt, oRang As Range, n%, m%  由于笔误,i变量不是整型变量,是段落对象。现把这句更正为:Dim i As Paragraph, mt, oRang As Range, n%, m%
查找替换原理:
将一个 Range 对象变量设置为等于另一个 Range 对象变量
下列指令(语句)将名为 Range2 的区域变量设为与 Range1 代表的位置相同。
Set Range2 = Range1   '译为:设置Range2对象与 Range1 代表的位置相同
现在两个变量代表同一区域。修改 Range2 的起点、终点或其中的文本将影响 Range1,反之亦然。

TA的精华主题

TA的得分主题

发表于 2016-11-19 21:54 | 显示全部楼层
____杜先生 你好!
____真心地说,你是最近一段时间以来最高水平的在线正则专家了!名符其实,实至名归!
____最近看到 守柔版主 的一篇测试文章,说正则比普通查找快74倍!此帖发布好多天了,无奈我未引起重视,因为我看不懂你的正则代码。刚才试验一番,发现你的代码中除正则部分外,还在用 For Each...Next 循环。这个循环确实好,以前我把它奉为至宝,但最近我觉得在 Word 查找和替换操作中,Do While...Loop 循环 + 查找操作比较好,感觉会比 For Each...Next 循环快,如果头部换上正则,是不是更快呢?
____下面我献上最近的拙作《第一条》代码,请 杜先生 修改一下(加个正则表达式的头部 + Do While...Loop 循环):
  1. Sub 第一条()
  2.     Selection.HomeKey unit:=wdStory
  3.     Selection.Find.ClearFormatting
  4.     Do While Selection.Find.Execute(findtext:=vbCr & "第[一二三四五六七八九十百零千0-9]@条", Forward:=True, MatchWildcards:=True)
  5.         Selection.MoveStart unit:=wdCharacter, Count:=1
  6.         Selection.InsertAfter Text:=Chr(-24159)
  7.         Selection.MoveEnd unit:=wdCharacter, Count:=-1
  8.         With Selection.Font
  9.             .Name = "黑体"
  10.             .Name = "Times New Roman"
  11.             .Bold = True
  12.             .Color = wdColorPink '粉红
  13.         End With
  14.         Selection.EndKey unit:=wdLine
  15.     Loop
  16. End Sub
复制代码

TA的精华主题

TA的得分主题

发表于 2016-11-19 22:07 | 显示全部楼层
补:杜先生,守柔版主 文章中说,查找时用域(Range)会比用 Selection 对象慢一些,我实测后感觉也是如此,杜先生认为呢?(我看 杜先生 代码中都是用 0Rang 域的。)

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-11-19 22:35 | 显示全部楼层
本帖最后由 duquancai 于 2016-11-19 22:37 编辑
413191246se 发表于 2016-11-19 21:54
____杜先生 你好!
____真心地说,你是最近一段时间以来最高水平的在线正则专家了!名符其实,实至名归!
...

我并不是热衷于for 循环,其实我也很喜欢do循环。我有些回帖使用的就没有遍历每一个段落(直接整个文档作为的目标文本),原因如下:
用正则的时候
1、word文档中,如果有表格并且需要查找的内容有可能会在表格中,使用正则时 那么就得遍历每一个段落(包括表格中的单元格中的每个段落)以每一个段落作为“目标文本”,否则在整个文档中(这是候目标文本为整个文档)使用正则查找到的是在表格中的字符“位置定位就定位不准了!所以要遍历每一个段落!”。
2、如果Word文档中无表格或者说就算有表格但是需要查找的内容根本不可能在表格中出现,那么就不用遍历文档中的每一个段落了,直接把“整个文档当做目标文本进行查找”,我想在这种情况下正则查找要快很多!这也是其中一个原因吧!

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-11-19 22:42 | 显示全部楼层
本帖最后由 duquancai 于 2016-11-19 22:45 编辑
413191246se 发表于 2016-11-19 22:07
补:杜先生,守柔版主 文章中说,查找时用域(Range)会比用 Selection 对象慢一些,我实测后感觉也是如此 ...

你可能误会了,我并没有用“域”,那是用的range对象变量。其实我个人:不太喜欢selection对象和select方法,我跟热衷于range对象。但是调试程序和学习其它对象、方法、属性的时候,用selection对象和select方法是比较好的,如果熟悉了,我认为没必要用selection对象和select方法,但是有时候也不得不用!!!

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-11-19 23:01 | 显示全部楼层
413191246se 发表于 2016-11-19 21:54
____杜先生 你好!
____真心地说,你是最近一段时间以来最高水平的在线正则专家了!名符其实,实至名归!
...

如果Word文档中无表格或者说就算有表格但是需要查找的内容根本不可能在表格中出现,代码可以:
  1. Sub Word文档中修改格式()
  2.    Dim mt, oRang As Range, n%, m%, Crang As Range
  3.    Set Crang = ActiveDocument.Content
  4.    With CreateObject("vbscript.regexp")
  5.       .Pattern = "^第[一二三四五六七八九十百零千\d]+条"
  6.       .Global = True: .MultiLine = True
  7.       For Each mt In .Execute(Crang)
  8.          m = mt.FirstIndex: n = mt.Length
  9.          Set oRang = ActiveDocument.Range(Crang.Start + m, Crang.Start + m + n)
  10.          With oRang.Font
  11.             .Name = "黑体"
  12.             .Name = "Times New Roman"
  13.             .Bold = True
  14.             .Color = wdColorPink
  15.          End With
  16.       Next
  17.    End With
  18. End Sub
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-11-19 23:44 | 显示全部楼层
413191246se 发表于 2016-11-19 21:54
____杜先生 你好!
____真心地说,你是最近一段时间以来最高水平的在线正则专家了!名符其实,实至名归!
...

不好意思,我没注意看你的代码,你不但修改了格式还添加了字符,不过你使用的通配符查找。要是比如“第一条”这个是在文档首,且“第一条”之前没有回车符,你的代码是应该查找不到的。正则能够弥补Word通配符的不足,所以我喜欢正则,呵呵!我重新写了一个代码,你测试:

  1. Sub Word文档中查找替换并设置格式()
  2.    Dim col As New Collection, k&, Crng As Range
  3.    Dim mt, oRang As Range, j%, n%, m%
  4.    Set Crng = ActiveDocument.Content
  5.    With CreateObject("vbscript.regexp")
  6.       .Pattern = "^第[一二三四五六七八九十百零千\d]+条"
  7.       .Global = True: .MultiLine = True
  8.       For Each mt In .Execute(Crng)
  9.          k = k + 1
  10.          m = mt.FirstIndex: n = mt.Length
  11.          Set oRang = ActiveDocument.Range(Crng.Start + m, Crng.Start + m + n)
  12.          col.Add oRang, CStr(k)
  13.       Next
  14.    End With
  15.    For j = 1 To col.Count
  16.       With col(j).Font
  17.          .Name = "黑体"
  18.          .Name = "Times New Roman"
  19.          .Bold = True
  20.          .Color = wdColorPink
  21.          col(j).InsertAfter Text:=Chr(-24159)
  22.       End With
  23.    Next
  24. End Sub
复制代码

评分

2

查看全部评分

TA的精华主题

TA的得分主题

发表于 2016-11-20 00:07 | 显示全部楼层
具体Selection对象和Range对象的效率差别,最好是自己谢谢代码对比一下
记忆中我好想做过demo对比过,在不关闭屏幕刷新application.screenupdating  = false的时候
select selection效率会比range引用低很多很多,在关闭屏幕刷新的情况下,差别就没有特别明显。
具体当时的结论我也不清楚了,反正不是绝对必要,我是不会使用selection的,select方法是100%不用的
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-21 18:59 , Processed in 0.035278 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表