WORD X: VBA macro including find/replace with styles much slower than regular macro
Author |
Message |
John McGhie [MVP - Word #1 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
Hi Pierre: OK, I think I can see the problem. I am cross-posting this into two of the VBA groups where people with better coding skills than mine can take a look (fellas: this is Mac Word v.X, which has Word 2000's VBA less the Enums). This responds to microsoft.public.mac.office.word on 20 Jul 2002 06:53:25
In your webStyles() macro, you are setting two variables then calling styleToTag. I think this causes styleToTag to recompile on each iteration. I think you will get a speedup if you set up a two-dimensional array within styleToTag, and add another level of nested "for each..." to walk down the array. The only other suggestion I can make is to work with the Selection object rather than the Range object. For reasons we cannot figure out, the Selection object is dramatically faster. This is counter-intuitive to me: I think I smell a large lump of inline assembler in the mechanism for the Selection Find object :-) Others who know much more about VBA will have more to say on this. Cheers Quote: > Since I often have to publish texts both in printed form and for the > Web, I use both character styles and paragraph styles in Word for > every element in my text that will need a specific tag in HTML. > When it comes time to "prepare" my texts for HTML editing (which I do > in BBEdit), I then use a couple of VBA macros to search for > occurrences of these styles and add the tags to the occurrences before > transferring the text to BBEdit (which will strip it of all its > formatting). > My problem is that I find that VBA's Find/Replace commands work much > slower when style formatting is involved. > For example, here's the code I use for Find/Replace with styles: > ----- > Sub webStyles() > styleToTag myStyle:="Emphasis", myTag:="em" > styleToTag myStyle:="Italics", myTag:="i" > styleToTag myStyle:="Bold", myTag:="b" > styleToTag myStyle:="Strong", myTag:="strong" > styleToTag myStyle:="Cite", myTag:="cite" > styleToTag myStyle:="Book Title - English", myTag:="i" > styleToTag myStyle:="Book Title - French", myTag:="i" > paraStyleToTag myStyle:="Citation", myTag:="blockquote" > paraStyleToTag myStyle:="Heading 1", myTag:="h1" > paraStyleToTag myStyle:="Heading 2", myTag:="h2" > paraStyleToTag myStyle:="Heading 3", myTag:="h3" > End Sub > Sub styleToTag(myStyle, myTag) > For Each sty In ActiveDocument.Styles > If sty = myStyle Then > With Selection.Find > .ClearFormatting > .Style = ActiveDocument.Styles(myStyle) > .Replacement.ClearFormatting > .Replacement.Style = ActiveDocument.Styles(myStyle) > .Text = "" > .Replacement.Text = "<" & myTag & ">^&</" & myTag & ">" > .Forward = True > .Wrap = wdFindStop > .MatchWholeWord = False > .MatchWildcards = False > .MatchSoundsLike = False > .MatchAllWordForms = False > .Execute Replace:=wdReplaceAll > End With > End If > Next sty > End Sub > Sub paraStyleToTag(myStyle, myTag) > If Selection.Type <> wdSelectionIP Then > For I = 1 To Selection.Paragraphs.Count > If Selection.Paragraphs(I).Style = myStyle Then > Set myPara = Selection.Paragraphs(I).Range > myPara.MoveEnd Unit:=wdCharacter, Count:=-1 > With myPara > .InsertBefore Text:=("<" & myTag & ">") > .InsertAfter Text:=("</" & myTag & ">") > End With > End If > Next I > Else > MsgBox "No selection." > End If > End Sub > ----- > As a comparison, here's the code for another macro I use to "prepare" > the punctuation of the text for Web publishing. This macro does NOT > involve any style formatting, just plain old find/replace of textual > elements. > ----- > Sub webPunctuation() > With Selection.Find > .ClearFormatting > .Replacement.ClearFormatting > .MatchCase = False > End With > characterSwitch myOld:="&", myNew:="&" > characterSwitch myOld:="^s", myNew:=" " > characterSwitch myOld:="(c)", myNew:="©" > characterSwitch myOld:="(tm)", myNew:="&tm;" > characterSwitch myOld:="(r)", myNew:="®" > End Sub > Sub characterSwitch(myOld, myNew) > With Selection.Find > .Text = myOld > .Replacement.Text = myNew > .Forward = True > .Wrap = wdFindStop > .MatchCase = False > .Format = False > .MatchWholeWord = False > .MatchWildcards = False > .MatchSoundsLike = False > .MatchAllWordForms = False > End With > Selection.Find.Execute Replace:=wdReplaceAll > End Sub > ---- > This second macro is MUCH faster than the first one. I would say that > it is about TEN TIMES faster. For a short text, it is almost > instantaneous (in Word X SR1), whereas the macro involving styles last > longer than 10 seconds. For longer articles, it's even worse. > Is this "normal"? > Pierre > --- > LATEXT - Literature and Visual Arts http://www.*-*-*.com/ > "Apple Peel" Columnist at Applelust.com
Please post all comments to the newsgroup to maintain the thread. John McGhie, Consultant Technical Writer McGhie Information Engineering Pty Ltd Sydney, Australia. GMT + 10 Hrs
|
Fri, 07 Jan 2005 07:57:00 GMT |
|
|
Anto #2 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
Hello Pierre & John
Quote: > Hi Pierre: > In your webStyles() macro, you are setting two variables then calling > styleToTag. I think this causes styleToTag to recompile on each iteration.
Is this right? I thought the VBA engine interpreted compiled bytecode. If what you say is true, then no wonder! Quote: > I think you will get a speedup if you set up a two-dimensional array within > styleToTag, and add another level of nested "for each..." to walk down the > array.
Do you mean so that it eliminates all the subroutine calls (sorry I have trouble visualising this sort of thing)? Pierre, your second example has half as many subroutine calls as the first. Also, the second (faster) example is effectively searching and replacing plain text. This is fast. Your first example is having to read (and write) style information contained in the paragraph as well. The style information is not stored with the characters. So presumably this means slower searching and replacing. But anyway, is 10 seconds to have Word format an article for you really a problem? Please note, however, that I've never had to write 'perfomance' VBA code. I've not investigated this, just guessing based on what I think would be logical (often the cause of much grief, will I ever learn? :). Regards, Antony ~~~~~~~~~~ John McGhie continued... Quote: > The only other suggestion I can make is to work with the Selection object > rather than the Range object. For reasons we cannot figure out, the > Selection object is dramatically faster. This is counter-intuitive to me: I > think I smell a large lump of inline assembler in the mechanism for the > Selection Find object :-) > Others who know much more about VBA will have more to say on this. > Cheers > > Since I often have to publish texts both in printed form and for the > > Web, I use both character styles and paragraph styles in Word for > > every element in my text that will need a specific tag in HTML. > > When it comes time to "prepare" my texts for HTML editing (which I do > > in BBEdit), I then use a couple of VBA macros to search for > > occurrences of these styles and add the tags to the occurrences before > > transferring the text to BBEdit (which will strip it of all its > > formatting). > > My problem is that I find that VBA's Find/Replace commands work much > > slower when style formatting is involved. > > For example, here's the code I use for Find/Replace with styles: > > ----- > > Sub webStyles() > > styleToTag myStyle:="Emphasis", myTag:="em" > > styleToTag myStyle:="Italics", myTag:="i" > > styleToTag myStyle:="Bold", myTag:="b" > > styleToTag myStyle:="Strong", myTag:="strong" > > styleToTag myStyle:="Cite", myTag:="cite" > > styleToTag myStyle:="Book Title - English", myTag:="i" > > styleToTag myStyle:="Book Title - French", myTag:="i" > > paraStyleToTag myStyle:="Citation", myTag:="blockquote" > > paraStyleToTag myStyle:="Heading 1", myTag:="h1" > > paraStyleToTag myStyle:="Heading 2", myTag:="h2" > > paraStyleToTag myStyle:="Heading 3", myTag:="h3" > > End Sub > > Sub styleToTag(myStyle, myTag) > > For Each sty In ActiveDocument.Styles > > If sty = myStyle Then > > With Selection.Find > > .ClearFormatting > > .Style = ActiveDocument.Styles(myStyle) > > .Replacement.ClearFormatting > > .Replacement.Style = ActiveDocument.Styles(myStyle) > > .Text = "" > > .Replacement.Text = "<" & myTag & ">^&</" & myTag & ">" > > .Forward = True > > .Wrap = wdFindStop > > .MatchWholeWord = False > > .MatchWildcards = False > > .MatchSoundsLike = False > > .MatchAllWordForms = False > > .Execute Replace:=wdReplaceAll > > End With > > End If > > Next sty > > End Sub > > Sub paraStyleToTag(myStyle, myTag) > > If Selection.Type <> wdSelectionIP Then > > For I = 1 To Selection.Paragraphs.Count > > If Selection.Paragraphs(I).Style = myStyle Then > > Set myPara = Selection.Paragraphs(I).Range > > myPara.MoveEnd Unit:=wdCharacter, Count:=-1 > > With myPara > > .InsertBefore Text:=("<" & myTag & ">") > > .InsertAfter Text:=("</" & myTag & ">") > > End With > > End If > > Next I > > Else > > MsgBox "No selection." > > End If > > End Sub > > ----- > > As a comparison, here's the code for another macro I use to "prepare" > > the punctuation of the text for Web publishing. This macro does NOT > > involve any style formatting, just plain old find/replace of textual > > elements. > > ----- > > Sub webPunctuation() > > With Selection.Find > > .ClearFormatting > > .Replacement.ClearFormatting > > .MatchCase = False > > End With > > characterSwitch myOld:="&", myNew:="&" > > characterSwitch myOld:="^s", myNew:=" " > > characterSwitch myOld:="(c)", myNew:="©" > > characterSwitch myOld:="(tm)", myNew:="&tm;" > > characterSwitch myOld:="(r)", myNew:="®" > > End Sub > > Sub characterSwitch(myOld, myNew) > > With Selection.Find > > .Text = myOld > > .Replacement.Text = myNew > > .Forward = True > > .Wrap = wdFindStop > > .MatchCase = False > > .Format = False > > .MatchWholeWord = False > > .MatchWildcards = False > > .MatchSoundsLike = False > > .MatchAllWordForms = False > > End With > > Selection.Find.Execute Replace:=wdReplaceAll > > End Sub > > ---- > > This second macro is MUCH faster than the first one. I would say that > > it is about TEN TIMES faster. For a short text, it is almost > > instantaneous (in Word X SR1), whereas the macro involving styles last > > longer than 10 seconds. For longer articles, it's even worse. > > Is this "normal"? > > Pierre > > --- > > LATEXT - Literature and Visual Arts http://www.latext.com > > "Apple Peel" Columnist at Applelust.com > Please post all comments to the newsgroup to maintain the thread. > John McGhie, Consultant Technical Writer > McGhie Information Engineering Pty Ltd > Sydney, Australia. GMT + 10 Hrs
|
Sat, 08 Jan 2005 00:01:35 GMT |
|
|
John McGhie [MVP - Word #3 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
Hi Anthony: This responds to microsoft.public.mac.office.word on 22 Jul 2002 09:01:35
Quote: > > In your webStyles() macro, you are setting two variables then calling > > styleToTag. I think this causes styleToTag to recompile on each iteration. > Is this right? I thought the VBA engine interpreted compiled bytecode. > If what you say is true, then no wonder!
It does. Each module is compiled at first call. But there's complicated rules for when the compiled bytecode persists between calls and when it is re-compiled. One of the reasons I cross-posted was that I hoped that you blokes over there would know :-) Quote: > Do you mean so that it eliminates all the subroutine calls (sorry I > have trouble visualising this sort of thing)?
Yeah. Within a subroutine, it should automatically pass parameters by reference, calling externally I wonder if it is trying to pass byVal? Quote: > the second (faster) > example is effectively searching and replacing plain text. This is fast.
Given that it is not opening the ParagraphFormatting object at all, yes. But I thought it obtained these things by pointer anyway, which is very quick because the pointers and formatting tables should all be resident in memory. I don't notice the thing being slow when I search for formatting, but maybe I am not looking properly. Cheers Please post all comments to the newsgroup to maintain the thread. John McGhie, Consultant Technical Writer McGhie Information Engineering Pty Ltd Sydney, Australia. GMT + 10 Hrs
|
Sat, 08 Jan 2005 07:49:29 GMT |
|
|
Anto #4 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
Hello Pierre I have never bothered with VBA performance issues because these days I use it for dead simple stuff and batch processing that I'm happy to leave running in the backround. But you've piqued my interest so I've had a look at your code. I'm afraid the problem isn't VBA, it's your algorithms. [Aside:
> It does. Each module is compiled at first call. But there's complicated > rules for when the compiled bytecode persists between calls and when it is > re-compiled. One of the reasons I cross-posted was that I hoped that you > blokes over there would know :-) So what does Debug | Compile Project do? Can't find any info on it anywhere.] paraStyleToTag -------------- Your algorithm is very slow. On my 200 page test document I got bored of waiting for it to finish and broke out of it after several minutes. My version below does it in about one and a half seconds (the paragraph bit) by using the following techniques. Your paraStyleToTag loops over all the paragraphs in the selection every time it's called and looks for the matching style. But really you only need to loop over the paragraphs once, and check for all the styles in one go. Place the names of the styles you want to search for and the replacement tags in a 2-dimensional array. I think this is what John McGhie suggested. Put the most common styles at the beginning of the array. So the webStyles macro could be written as follows. (I've left the styleToTag stuff as it is for the moment.) Sub webStyles() Const PARASTYLE As Integer = 0 Const MYTAG As Integer = 1 Dim para As Paragraph Dim my_array_of_styles As Variant Dim i As Integer my_array_of_styles = Array( _ Array("Citation", "blockquote"), _ Array("Heading 1", "h1"), _ Array("Heading 2", "h2"), _ Array("Heading 3", "h3") _ ) styleToTag myStyle:="Emphasis", MYTAG:="em" styleToTag myStyle:="Italics", MYTAG:="i" styleToTag myStyle:="Bold", MYTAG:="b" styleToTag myStyle:="Strong", MYTAG:="strong" styleToTag myStyle:="Cite", MYTAG:="cite" styleToTag myStyle:="Book Title - English", MYTAG:="i" styleToTag myStyle:="Book Title - French", MYTAG:="i" If Selection.Type = wdSelectionIP Then MsgBox "no selection" Else For Each para In Selection.Paragraphs For i = 0 To UBound(my_array_of_styles) If para.Style = my_array_of_styles(i)(PARASTYLE) Then With para.Range .MoveEnd Unit:=wdCharacter, Count:=-1 .InsertBefore Text:=("<" & my_array_of_styles(i)(MYTAG) & ">") .InsertAfter Text:=("</" & my_array_of_styles(i)(MYTAG) & ">") End With End If Next i Next para End If End Sub So now there are no subroutine calls for the paragraph formatting and you loop through the paragraphs only once. styleToTag ---------- This will benefit from a similar approach. But also you are looping through every style in the document (of which there are quite a lot) for every call to the routine. You do this to double-check that the style you are searching for is actually defined. This checking is gives a performance hit. You don't need it. Assume that all the styles are defined (because most of them will be) and use an error handler to take care of the case when they are not. Sub styleToTag(myStyle, myTag) On Error GoTo err_handler With Selection.Find .ClearFormatting .Style = ActiveDocument.Styles(myStyle) .Replacement.ClearFormatting .Replacement.Style = ActiveDocument.Styles(myStyle) .Text = "" .Replacement.Text = "<" & myTag & ">^&</" & myTag & ">" .Forward = True .Wrap = wdFindStop .MatchWholeWord = False .MatchWildcards = False .MatchSoundsLike = False .MatchAllWordForms = False .Execute Replace:=wdReplaceAll End With err_handler: If Err.Number = 5941 Then 'oops, error: style doesn't 'exist Resume Next 'can safely ignore it, 'carry on with processing Else 'mustn't ignore any other errors On Error GoTo 0 'so turn off error trapping, Resume 'and let errors appear as normal End If End Sub Well, testing on my 200 page document this alone saves you a second and a half without implementing the techniques I used for paraStyleToTag. So yes, you were right John, searching for styles is inherently quick. Hope this helps Pierre. Antony ~~~~~~~~~~
Quote: > Hi Anthony: > This responds to microsoft.public.mac.office.word on 22 Jul 2002 09:01:35
> > > In your webStyles() macro, you are setting two variables then calling > > > styleToTag. I think this causes styleToTag to recompile on each iteration. > > Is this right? I thought the VBA engine interpreted compiled bytecode. > > If what you say is true, then no wonder! > It does. Each module is compiled at first call. But there's complicated > rules for when the compiled bytecode persists between calls and when it is > re-compiled. One of the reasons I cross-posted was that I hoped that you > blokes over there would know :-) > > Do you mean so that it eliminates all the subroutine calls (sorry I > > have trouble visualising this sort of thing)? > Yeah. Within a subroutine, it should automatically pass parameters by > reference, calling externally I wonder if it is trying to pass byVal? > > the second (faster) > > example is effectively searching and replacing plain text. This is fast. > Given that it is not opening the ParagraphFormatting object at all, yes. > But I thought it obtained these things by pointer anyway, which is very > quick because the pointers and formatting tables should all be resident in > memory. I don't notice the thing being slow when I search for formatting, > but maybe I am not looking properly. > Cheers > Please post all comments to the newsgroup to maintain the thread. > John McGhie, Consultant Technical Writer > McGhie Information Engineering Pty Ltd > Sydney, Australia. GMT + 10 Hrs
|
Sat, 08 Jan 2005 20:23:08 GMT |
|
|
Howard Kaiko #5 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
It is my understanding that all of the Mac VBA versions were based on VBA 5, not VBA 6. -- http://www.standards.com/; Programming and support for Word macros, including converting from WordBasic to VBA; Technical reviewing; Standards; Product functional/design/specifications ------------------------------------------------
Quote: > Hi Pierre: > OK, I think I can see the problem. I am cross-posting this into two of the > VBA groups where people with better coding skills than mine can take a look > (fellas: this is Mac Word v.X, which has Word 2000's VBA less the Enums). > This responds to microsoft.public.mac.office.word on 20 Jul 2002 06:53:25
> In your webStyles() macro, you are setting two variables then calling > styleToTag. I think this causes styleToTag to recompile on each iteration. > I think you will get a speedup if you set up a two-dimensional array within > styleToTag, and add another level of nested "for each..." to walk down the > array. > The only other suggestion I can make is to work with the Selection object > rather than the Range object. For reasons we cannot figure out, the > Selection object is dramatically faster. This is counter-intuitive to me: I > think I smell a large lump of inline assembler in the mechanism for the > Selection Find object :-) > Others who know much more about VBA will have more to say on this. > Cheers > > Since I often have to publish texts both in printed form and for the > > Web, I use both character styles and paragraph styles in Word for > > every element in my text that will need a specific tag in HTML. > > When it comes time to "prepare" my texts for HTML editing (which I do > > in BBEdit), I then use a couple of VBA macros to search for > > occurrences of these styles and add the tags to the occurrences before > > transferring the text to BBEdit (which will strip it of all its > > formatting). > > My problem is that I find that VBA's Find/Replace commands work much > > slower when style formatting is involved. > > For example, here's the code I use for Find/Replace with styles: > > ----- > > Sub webStyles() > > styleToTag myStyle:="Emphasis", myTag:="em" > > styleToTag myStyle:="Italics", myTag:="i" > > styleToTag myStyle:="Bold", myTag:="b" > > styleToTag myStyle:="Strong", myTag:="strong" > > styleToTag myStyle:="Cite", myTag:="cite" > > styleToTag myStyle:="Book Title - English", myTag:="i" > > styleToTag myStyle:="Book Title - French", myTag:="i" > > paraStyleToTag myStyle:="Citation", myTag:="blockquote" > > paraStyleToTag myStyle:="Heading 1", myTag:="h1" > > paraStyleToTag myStyle:="Heading 2", myTag:="h2" > > paraStyleToTag myStyle:="Heading 3", myTag:="h3" > > End Sub > > Sub styleToTag(myStyle, myTag) > > For Each sty In ActiveDocument.Styles > > If sty = myStyle Then > > With Selection.Find > > .ClearFormatting > > .Style = ActiveDocument.Styles(myStyle) > > .Replacement.ClearFormatting > > .Replacement.Style = ActiveDocument.Styles(myStyle) > > .Text = "" > > .Replacement.Text = "<" & myTag & ">^&</" & myTag & ">" > > .Forward = True > > .Wrap = wdFindStop > > .MatchWholeWord = False > > .MatchWildcards = False > > .MatchSoundsLike = False > > .MatchAllWordForms = False > > .Execute Replace:=wdReplaceAll > > End With > > End If > > Next sty > > End Sub > > Sub paraStyleToTag(myStyle, myTag) > > If Selection.Type <> wdSelectionIP Then > > For I = 1 To Selection.Paragraphs.Count > > If Selection.Paragraphs(I).Style = myStyle Then > > Set myPara = Selection.Paragraphs(I).Range > > myPara.MoveEnd Unit:=wdCharacter, Count:=-1 > > With myPara > > .InsertBefore Text:=("<" & myTag & ">") > > .InsertAfter Text:=("</" & myTag & ">") > > End With > > End If > > Next I > > Else > > MsgBox "No selection." > > End If > > End Sub > > ----- > > As a comparison, here's the code for another macro I use to "prepare" > > the punctuation of the text for Web publishing. This macro does NOT > > involve any style formatting, just plain old find/replace of textual > > elements. > > ----- > > Sub webPunctuation() > > With Selection.Find > > .ClearFormatting > > .Replacement.ClearFormatting > > .MatchCase = False > > End With > > characterSwitch myOld:="&", myNew:="&" > > characterSwitch myOld:="^s", myNew:=" " > > characterSwitch myOld:="(c)", myNew:="©" > > characterSwitch myOld:="(tm)", myNew:="&tm;" > > characterSwitch myOld:="(r)", myNew:="®" > > End Sub > > Sub characterSwitch(myOld, myNew) > > With Selection.Find > > .Text = myOld > > .Replacement.Text = myNew > > .Forward = True > > .Wrap = wdFindStop > > .MatchCase = False > > .Format = False > > .MatchWholeWord = False > > .MatchWildcards = False > > .MatchSoundsLike = False > > .MatchAllWordForms = False > > End With > > Selection.Find.Execute Replace:=wdReplaceAll > > End Sub > > ---- > > This second macro is MUCH faster than the first one. I would say that > > it is about TEN TIMES faster. For a short text, it is almost > > instantaneous (in Word X SR1), whereas the macro involving styles last > > longer than 10 seconds. For longer articles, it's even worse. > > Is this "normal"? > > Pierre > > --- > > LATEXT - Literature and Visual Arts http://www.latext.com > > "Apple Peel" Columnist at Applelust.com > Please post all comments to the newsgroup to maintain the thread. > John McGhie, Consultant Technical Writer > McGhie Information Engineering Pty Ltd > Sydney, Australia. GMT + 10 Hrs
|
Sat, 08 Jan 2005 21:28:03 GMT |
|
|
Paul Berkowit #6 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
That is correct. It's the same VBA as Word 97, not Word 2000 without the enums as John said. -- Paul Berkowitz MVP Entourage Please "Reply To Newsgroup" to reply to this message. Emails will be ignored. PLEASE always state which version of Entourage you are using - 2001 or X. It's often impossible to answer your questions otherwise. Quote:
> Newsgroups: > microsoft.public.mac.office.word,microsoft.public.word.vba.customization,mi cro > soft.public.word.vba.general > Date: Tue, 23 Jul 2002 09:28:03 -0400 > Subject: Re: WORD X: VBA macro including find/replace with styles much slower > than regular macro > It is my understanding that all of the Mac VBA versions were based on VBA 5, > not VBA 6. > -- > http://www.standards.com/; Programming and support for Word macros, > including converting from WordBasic to VBA; Technical reviewing; Standards; > Product functional/design/specifications > ------------------------------------------------
>> Hi Pierre: >> OK, I think I can see the problem. I am cross-posting this into two of > the >> VBA groups where people with better coding skills than mine can take a > look >> (fellas: this is Mac Word v.X, which has Word 2000's VBA less the Enums). >> This responds to microsoft.public.mac.office.word on 20 Jul 2002 06:53:25
>> In your webStyles() macro, you are setting two variables then calling >> styleToTag. I think this causes styleToTag to recompile on each > iteration. >> I think you will get a speedup if you set up a two-dimensional array > within >> styleToTag, and add another level of nested "for each..." to walk down the >> array. >> The only other suggestion I can make is to work with the Selection object >> rather than the Range object. For reasons we cannot figure out, the >> Selection object is dramatically faster. This is counter-intuitive to me: > I >> think I smell a large lump of inline assembler in the mechanism for the >> Selection Find object :-) >> Others who know much more about VBA will have more to say on this. >> Cheers >>> Since I often have to publish texts both in printed form and for the >>> Web, I use both character styles and paragraph styles in Word for >>> every element in my text that will need a specific tag in HTML. >>> When it comes time to "prepare" my texts for HTML editing (which I do >>> in BBEdit), I then use a couple of VBA macros to search for >>> occurrences of these styles and add the tags to the occurrences before >>> transferring the text to BBEdit (which will strip it of all its >>> formatting). >>> My problem is that I find that VBA's Find/Replace commands work much >>> slower when style formatting is involved. >>> For example, here's the code I use for Find/Replace with styles: >>> ----- >>> Sub webStyles() >>> styleToTag myStyle:="Emphasis", myTag:="em" >>> styleToTag myStyle:="Italics", myTag:="i" >>> styleToTag myStyle:="Bold", myTag:="b" >>> styleToTag myStyle:="Strong", myTag:="strong" >>> styleToTag myStyle:="Cite", myTag:="cite" >>> styleToTag myStyle:="Book Title - English", myTag:="i" >>> styleToTag myStyle:="Book Title - French", myTag:="i" >>> paraStyleToTag myStyle:="Citation", myTag:="blockquote" >>> paraStyleToTag myStyle:="Heading 1", myTag:="h1" >>> paraStyleToTag myStyle:="Heading 2", myTag:="h2" >>> paraStyleToTag myStyle:="Heading 3", myTag:="h3" >>> End Sub >>> Sub styleToTag(myStyle, myTag) >>> For Each sty In ActiveDocument.Styles >>> If sty = myStyle Then >>> With Selection.Find >>> .ClearFormatting >>> .Style = ActiveDocument.Styles(myStyle) >>> .Replacement.ClearFormatting >>> .Replacement.Style = ActiveDocument.Styles(myStyle) >>> .Text = "" >>> .Replacement.Text = "<" & myTag & ">^&</" & myTag & ">" >>> .Forward = True >>> .Wrap = wdFindStop >>> .MatchWholeWord = False >>> .MatchWildcards = False >>> .MatchSoundsLike = False >>> .MatchAllWordForms = False >>> .Execute Replace:=wdReplaceAll >>> End With >>> End If >>> Next sty >>> End Sub >>> Sub paraStyleToTag(myStyle, myTag) >>> If Selection.Type <> wdSelectionIP Then >>> For I = 1 To Selection.Paragraphs.Count >>> If Selection.Paragraphs(I).Style = myStyle Then >>> Set myPara = Selection.Paragraphs(I).Range >>> myPara.MoveEnd Unit:=wdCharacter, Count:=-1 >>> With myPara >>> .InsertBefore Text:=("<" & myTag & ">") >>> .InsertAfter Text:=("</" & myTag & ">") >>> End With >>> End If >>> Next I >>> Else >>> MsgBox "No selection." >>> End If >>> End Sub >>> ----- >>> As a comparison, here's the code for another macro I use to "prepare" >>> the punctuation of the text for Web publishing. This macro does NOT >>> involve any style formatting, just plain old find/replace of textual >>> elements. >>> ----- >>> Sub webPunctuation() >>> With Selection.Find >>> .ClearFormatting >>> .Replacement.ClearFormatting >>> .MatchCase = False >>> End With >>> characterSwitch myOld:="&", myNew:="&" >>> characterSwitch myOld:="^s", myNew:=" " >>> characterSwitch myOld:="(c)", myNew:="©" >>> characterSwitch myOld:="(tm)", myNew:="&tm;" >>> characterSwitch myOld:="(r)", myNew:="®" >>> End Sub >>> Sub characterSwitch(myOld, myNew) >>> With Selection.Find >>> .Text = myOld >>> .Replacement.Text = myNew >>> .Forward = True >>> .Wrap = wdFindStop >>> .MatchCase = False >>> .Format = False >>> .MatchWholeWord = False >>> .MatchWildcards = False >>> .MatchSoundsLike = False >>> .MatchAllWordForms = False >>> End With >>> Selection.Find.Execute Replace:=wdReplaceAll >>> End Sub >>> ---- >>> This second macro is MUCH faster than the first one. I would say that >>> it is about TEN TIMES faster. For a short text, it is almost >>> instantaneous (in Word X SR1), whereas the macro involving styles last >>> longer than 10 seconds. For longer articles, it's even worse. >>> Is this "normal"? >>> Pierre >>> --- >>> LATEXT - Literature and Visual Arts http://www.latext.com >>> "Apple Peel" Columnist at Applelust.com >> Please post all comments to the newsgroup to maintain the thread. >> John McGhie, Consultant Technical Writer >> McGhie Information Engineering Pty Ltd >> Sydney, Australia. GMT + 10 Hrs
|
Sun, 09 Jan 2005 11:37:06 GMT |
|
|
Anto #7 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
Sorry, forgot to add an Exit For for a little extra zip: If Selection.Type = wdSelectionIP Then MsgBox "no selection" Else For Each para In Selection.Paragraphs For i = 0 To UBound(my_array_of_styles) If para.Style = my_array_of_styles(i)(PARASTYLE) Then With para.Range .MoveEnd Unit:=wdCharacter, Count:=-1 .InsertBefore Text:=("<" & my_array_of_styles(i)(MYTAG) & ">") .InsertAfter Text:=("</" & my_array_of_styles(i)(MYTAG) & ">") End With Here>> Exit For End If Next i Next para End If Regards, Antony ~~~~~~~~~~ Quote:
> Hello Pierre > I have never bothered with VBA performance issues > because these days I use it for dead simple stuff > and batch processing that I'm happy to leave running > in the backround. But you've piqued my interest so > I've had a look at your code. I'm afraid the problem > isn't VBA, it's your algorithms. > [Aside:
> > It does. Each module is compiled at first call. But there's complicated > > rules for when the compiled bytecode persists between calls and when it is > > re-compiled. One of the reasons I cross-posted was that I hoped that you > > blokes over there would know :-) > So what does Debug | Compile Project do? Can't find > any info on it anywhere.] > paraStyleToTag > -------------- > Your algorithm is very slow. On my 200 page test > document I got bored of waiting for it to finish > and broke out of it after several minutes. My > version below does it in about one and a half > seconds (the paragraph bit) by using the following > techniques. > Your paraStyleToTag loops over all the paragraphs > in the selection every time it's called and looks > for the matching style. But really you only need > to loop over the paragraphs once, and check for > all the styles in one go. Place the names of the > styles you want to search for and the replacement > tags in a 2-dimensional array. I think this is what > John McGhie suggested. Put the most common styles at > the beginning of the array. So the webStyles macro > could be written as follows. (I've left the > styleToTag stuff as it is for the moment.) > Sub webStyles() > Const PARASTYLE As Integer = 0 > Const MYTAG As Integer = 1 > Dim para As Paragraph > Dim my_array_of_styles As Variant > Dim i As Integer > my_array_of_styles = Array( _ > Array("Citation", "blockquote"), _ > Array("Heading 1", "h1"), _ > Array("Heading 2", "h2"), _ > Array("Heading 3", "h3") _ > ) > styleToTag myStyle:="Emphasis", MYTAG:="em" > styleToTag myStyle:="Italics", MYTAG:="i" > styleToTag myStyle:="Bold", MYTAG:="b" > styleToTag myStyle:="Strong", MYTAG:="strong" > styleToTag myStyle:="Cite", MYTAG:="cite" > styleToTag myStyle:="Book Title - English", MYTAG:="i" > styleToTag myStyle:="Book Title - French", MYTAG:="i" > If Selection.Type = wdSelectionIP Then > MsgBox "no selection" > Else > For Each para In Selection.Paragraphs > For i = 0 To UBound(my_array_of_styles) > If para.Style = my_array_of_styles(i)(PARASTYLE) Then > With para.Range > .MoveEnd Unit:=wdCharacter, Count:=-1 > .InsertBefore Text:=("<" & my_array_of_styles(i)(MYTAG) & ">") > .InsertAfter Text:=("</" & my_array_of_styles(i)(MYTAG) & ">") > End With > End If > Next i > Next para > End If > End Sub > So now there are no subroutine calls for the paragraph > formatting and you loop through the paragraphs only once. > styleToTag > ---------- > This will benefit from a similar approach. But also > you are looping through every style in the document > (of which there are quite a lot) for every call to > the routine. You do this to double-check that the > style you are searching for is actually defined. This > checking is gives a performance hit. > You don't need it. Assume that all the styles are > defined (because most of them will be) and use > an error handler to take care of the case when they > are not. > Sub styleToTag(myStyle, myTag) > On Error GoTo err_handler > With Selection.Find > .ClearFormatting > .Style = ActiveDocument.Styles(myStyle) > .Replacement.ClearFormatting > .Replacement.Style = ActiveDocument.Styles(myStyle) > .Text = "" > .Replacement.Text = "<" & myTag & ">^&</" & myTag & ">" > .Forward = True > .Wrap = wdFindStop > .MatchWholeWord = False > .MatchWildcards = False > .MatchSoundsLike = False > .MatchAllWordForms = False > .Execute Replace:=wdReplaceAll > End With > err_handler: > If Err.Number = 5941 Then 'oops, error: style doesn't > 'exist > Resume Next 'can safely ignore it, > 'carry on with processing > Else 'mustn't ignore any other errors > On Error GoTo 0 'so turn off error trapping, > Resume 'and let errors appear as normal > End If > End Sub > Well, testing on my 200 page document this alone > saves you a second and a half without implementing > the techniques I used for paraStyleToTag. > So yes, you were right John, searching for styles > is inherently quick. > Hope this helps Pierre. > Antony > ~~~~~~~~~~
> > Hi Anthony: > > This responds to microsoft.public.mac.office.word on 22 Jul 2002 09:01:35
> > > > In your webStyles() macro, you are setting two variables then calling > > > > styleToTag. I think this causes styleToTag to recompile on each iteration. > > > Is this right? I thought the VBA engine interpreted compiled bytecode. > > > If what you say is true, then no wonder! > > It does. Each module is compiled at first call. But there's complicated > > rules for when the compiled bytecode persists between calls and when it is > > re-compiled. One of the reasons I cross-posted was that I hoped that you > > blokes over there would know :-) > > > Do you mean so that it eliminates all the subroutine calls (sorry I > > > have trouble visualising this sort of thing)? > > Yeah. Within a subroutine, it should automatically pass parameters by > > reference, calling externally I wonder if it is trying to pass byVal? > > > the second (faster) > > > example is effectively searching and replacing plain text. This is fast. > > Given that it is not opening the ParagraphFormatting object at all, yes. > > But I thought it obtained these things by pointer anyway, which is very > > quick because the pointers and formatting tables should all be resident in > > memory. I don't notice the thing being slow when I search for formatting, > > but maybe I am not looking properly. > > Cheers > > Please post all comments to the newsgroup to maintain the thread. > > John McGhie, Consultant Technical Writer > > McGhie Information Engineering Pty Ltd > > Sydney, Australia. GMT + 10 Hrs
|
Sun, 09 Jan 2005 17:33:30 GMT |
|
|
John McGhie [MVP #8 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
Hi Anthony: Lovely... Eloquent :-) Now *I* have a question: Would we squeeze a few more CPU cycles out of it if we moved the evaluation of Ubound(my-array-of-styles) out of the loop? Or you have chosen because it's a very short array that the overhead's not significant? Would it be quicker to use para.range.start.insertbefore and (para.range.end - 1).insert.after to perform the insertion directly without having to move the range? Or does that effectively re-evaluate the range anyway? I love this stuff :-) Many thanks
Quote: > Sorry, forgot to add an Exit For for a little > extra zip: > If Selection.Type = wdSelectionIP Then > MsgBox "no selection" > Else > For Each para In Selection.Paragraphs > For i = 0 To UBound(my_array_of_styles) > If para.Style = my_array_of_styles(i)(PARASTYLE) Then > With para.Range > .MoveEnd Unit:=wdCharacter, Count:=-1 > .InsertBefore Text:=("<" & my_array_of_styles(i)(MYTAG) & > ">") > .InsertAfter Text:=("</" & my_array_of_styles(i)(MYTAG) & > ">") > End With > Here>> Exit For > End If > Next i > Next para > End If > Regards, > Antony > ~~~~~~~~~~
>> Hello Pierre >> I have never bothered with VBA performance issues >> because these days I use it for dead simple stuff >> and batch processing that I'm happy to leave running >> in the backround. But you've piqued my interest so >> I've had a look at your code. I'm afraid the problem >> isn't VBA, it's your algorithms. >> [Aside:
>>> It does. Each module is compiled at first call. But there's complicated >>> rules for when the compiled bytecode persists between calls and when it is >>> re-compiled. One of the reasons I cross-posted was that I hoped that you >>> blokes over there would know :-) >> So what does Debug | Compile Project do? Can't find >> any info on it anywhere.] >> paraStyleToTag >> -------------- >> Your algorithm is very slow. On my 200 page test >> document I got bored of waiting for it to finish >> and broke out of it after several minutes. My >> version below does it in about one and a half >> seconds (the paragraph bit) by using the following >> techniques. >> Your paraStyleToTag loops over all the paragraphs >> in the selection every time it's called and looks >> for the matching style. But really you only need >> to loop over the paragraphs once, and check for >> all the styles in one go. Place the names of the >> styles you want to search for and the replacement >> tags in a 2-dimensional array. I think this is what >> John McGhie suggested. Put the most common styles at >> the beginning of the array. So the webStyles macro >> could be written as follows. (I've left the >> styleToTag stuff as it is for the moment.) >> Sub webStyles() >> Const PARASTYLE As Integer = 0 >> Const MYTAG As Integer = 1 >> Dim para As Paragraph >> Dim my_array_of_styles As Variant >> Dim i As Integer >> my_array_of_styles = Array( _ >> Array("Citation", "blockquote"), _ >> Array("Heading 1", "h1"), _ >> Array("Heading 2", "h2"), _ >> Array("Heading 3", "h3") _ >> ) >> styleToTag myStyle:="Emphasis", MYTAG:="em" >> styleToTag myStyle:="Italics", MYTAG:="i" >> styleToTag myStyle:="Bold", MYTAG:="b" >> styleToTag myStyle:="Strong", MYTAG:="strong" >> styleToTag myStyle:="Cite", MYTAG:="cite" >> styleToTag myStyle:="Book Title - English", MYTAG:="i" >> styleToTag myStyle:="Book Title - French", MYTAG:="i" >> If Selection.Type = wdSelectionIP Then >> MsgBox "no selection" >> Else >> For Each para In Selection.Paragraphs >> For i = 0 To UBound(my_array_of_styles) >> If para.Style = my_array_of_styles(i)(PARASTYLE) Then >> With para.Range >> .MoveEnd Unit:=wdCharacter, Count:=-1 >> .InsertBefore Text:=("<" & my_array_of_styles(i)(MYTAG) & >> ">") >> .InsertAfter Text:=("</" & my_array_of_styles(i)(MYTAG) & >> ">") >> End With >> End If >> Next i >> Next para >> End If >> End Sub >> So now there are no subroutine calls for the paragraph >> formatting and you loop through the paragraphs only once. >> styleToTag >> ---------- >> This will benefit from a similar approach. But also >> you are looping through every style in the document >> (of which there are quite a lot) for every call to >> the routine. You do this to double-check that the >> style you are searching for is actually defined. This >> checking is gives a performance hit. >> You don't need it. Assume that all the styles are >> defined (because most of them will be) and use >> an error handler to take care of the case when they >> are not. >> Sub styleToTag(myStyle, myTag) >> On Error GoTo err_handler >> With Selection.Find >> .ClearFormatting >> .Style = ActiveDocument.Styles(myStyle) >> .Replacement.ClearFormatting >> .Replacement.Style = ActiveDocument.Styles(myStyle) >> .Text = "" >> .Replacement.Text = "<" & myTag & ">^&</" & myTag & ">" >> .Forward = True >> .Wrap = wdFindStop >> .MatchWholeWord = False >> .MatchWildcards = False >> .MatchSoundsLike = False >> .MatchAllWordForms = False >> .Execute Replace:=wdReplaceAll >> End With >> err_handler: >> If Err.Number = 5941 Then 'oops, error: style doesn't >> 'exist >> Resume Next 'can safely ignore it, >> 'carry on with processing >> Else 'mustn't ignore any other errors >> On Error GoTo 0 'so turn off error trapping, >> Resume 'and let errors appear as normal >> End If >> End Sub >> Well, testing on my 200 page document this alone >> saves you a second and a half without implementing >> the techniques I used for paraStyleToTag. >> So yes, you were right John, searching for styles >> is inherently quick. >> Hope this helps Pierre. >> Antony >> ~~~~~~~~~~
>>> Hi Anthony: >>> This responds to microsoft.public.mac.office.word on 22 Jul 2002 09:01:35
>>>>> In your webStyles() macro, you are setting two variables then calling >>>>> styleToTag. I think this causes styleToTag to recompile on each >>>>> iteration. >>>> Is this right? I thought the VBA engine interpreted compiled bytecode. >>>> If what you say is true, then no wonder! >>> It does. Each module is compiled at first call. But there's complicated >>> rules for when the compiled bytecode persists between calls and when it is >>> re-compiled. One of the reasons I cross-posted was that I hoped that you >>> blokes over there would know :-) >>>> Do you mean so that it eliminates all the subroutine calls (sorry I >>>> have trouble visualising this sort of thing)? >>> Yeah. Within a subroutine, it should automatically pass parameters by >>> reference, calling externally I wonder if it is trying to pass byVal? >>>> the second (faster) >>>> example is effectively searching and replacing plain text. This is fast. >>> Given that it is not opening the ParagraphFormatting object at all, yes. >>> But I thought it obtained these things by pointer anyway, which is very >>> quick because the pointers and formatting tables should all be resident in >>> memory. I don't notice the thing being slow when I search for formatting, >>> but maybe I am not looking properly. >>> Cheers >>> Please post all comments to the newsgroup to maintain the thread. >>> John McGhie, Consultant Technical Writer >>> McGhie Information Engineering Pty Ltd >>> Sydney, Australia. GMT + 10 Hrs
-- Please post replies to the newsgroup to maintain the thread. John McGhie, Microsoft MVP: Word for Macintosh and Word for Windows Consultant Technical Writer
+61 4 1209 1410; Sydney, Australia: GMT + 10 hrs
|
Sun, 09 Jan 2005 21:24:05 GMT |
|
|
Jonathan Wes #9 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
Quote: > Hi Anthony: > Lovely... Eloquent :-) > Now *I* have a question: Would we squeeze a few more CPU cycles out of it if > we moved the evaluation of Ubound(my-array-of-styles) out of the loop? Or > you have chosen because it's a very short array that the overhead's not > significant?
I suspect that this will make little difference. It is a traditional code optimisation technique, but with Word VBA macros, the commands that manipulate numbers and strings are already lightning fast compared to those which manipulate the Word object model. Even as it is, the UBound function is only evaluated once per paragraph, on first entry to the inner loop. Quote: > Would it be quicker to use para.range.start.insertbefore and > (para.range.end - 1).insert.after to perform the insertion directly without > having to move the range? Or does that effectively re-evaluate the range > anyway?
This is a more fruitful line of attack. However, better still might be to use this to insert at the end of the para .Characters.Last.InsertBefore Text:=("</" & my_array_of_styles(i)(MYTAG) & ">") This works because .Characters.Last is a range marking the last character of the paragraph i.e. the paragraph mark. Use InsertBefore to insert the tag in front of it. One other speedup approach might be worth trying. Using a For Each loop to go through one of Word's built in collections can be extremely slow for larger collections. It may be faster to use the paragraphs.next property to get the location of the next paragraph on from the current one, and then use the InRange method to see whether the next paragraph is still inside the selection. The outer loop would therefore become a Do While loop rather than a For Each Next loop. If there are more than a couple of hundred paragraphs in the selection you should see a noticeable difference. If there are more than a thousand, you should see a difference sufficient to transform the performance of the macro. There is one problem however with this approach. If the selection contains a table with vertically merged cells, then the next paragraph within the table can take you back to a cell that has already been checked. You need to have code that guards against this. Its simple enough to do, you just check the value of .Range.End aganst that of the previous paragraph. If it is less, you need to take alternative action to move on. Quote: <aol>Me too!</aol> -- Regards Jonathan West - Word MVP MultiLinker - Automated generation of hyperlinks in Word Conversion to PDF & HTML http://www.multilinker.com Word FAQs at http://www.multilinker.com/wordfaq Please post any follow-up in the newsgroup. I do not reply to Word questions by email
|
Sun, 09 Jan 2005 22:33:18 GMT |
|
|
Pierre Ig #10 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
Quote:
> Sorry, forgot to add an Exit For for a little > extra zip: > If Selection.Type = wdSelectionIP Then > MsgBox "no selection" > Else > For Each para In Selection.Paragraphs > For i = 0 To UBound(my_array_of_styles) > If para.Style = my_array_of_styles(i)(PARASTYLE) Then > With para.Range > .MoveEnd Unit:=wdCharacter, Count:=-1 > .InsertBefore Text:=("<" & my_array_of_styles(i)(MYTAG) & ">") > .InsertAfter Text:=("</" & my_array_of_styles(i)(MYTAG) & ">") > End With > Here>> Exit For > End If > Next i > Next para > End If > Regards, > Antony
Thanks for all the suggestions, Antony. I will try them out whenever I get the chance. This confirms my suspicions, however, i.e. that macro programming in VBA is not for the faint-hearted :). Pierre --- LATEXT - Literature and Visual Arts http://www.latext.com "Apple Peel" Columnist at Applelust.com
|
Mon, 10 Jan 2005 02:48:31 GMT |
|
|
abc #11 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
Hello all, (Still the same me!) Quote: ----- Original Message -----
Newsgroups: microsoft.public.mac.office.word,microsoft.public.word.vba.customization,mic rosoft.public.word.vba.general Sent: Wednesday, July 24, 2002 3:33 PM Subject: Re: WORD X: VBA macro including find/replace with styles muchslower than regular macro
> > Hi Anthony: > > Lovely... Eloquent :-) > > Now *I* have a question: Would we squeeze a few more CPU cycles out of it > if > > we moved the evaluation of Ubound(my-array-of-styles) out of the loop? Or > > you have chosen because it's a very short array that the overhead's not > > significant? > I suspect that this will make little difference. It is a traditional code > optimisation technique, but with Word VBA macros, the commands that > manipulate numbers and strings are already lightning fast compared to those > which manipulate the Word object model. Even as it is, the UBound function > is only evaluated once per paragraph, on first entry to the inner loop. Agreed. Things like ubound() don't cause bottlenecks unless they are being called time and time again in a tight loop. I don't think it's really getting hammered in this instance. (On the other hand I unwittingly got hammered by the lookup functions when I first start programming with Access; they seemed so convenient... So sometimes it does pay to investigate this sort of thing.) But anyway, seeing as your definition of the array is hardcoded, you could have the upper bound of the array as a constant, also hard coded. But I don't like magic numbers in code (that's why I defined the array the way I did, almost out of petulance!). Once you've got the basic algorithm sorted out, all other optimization is a case of diminishing returns at the expense of readability and maintainabilty. Any optimizations should be carefully commented. Plus I wanted to leave as much of Pierre's original code intact so that my ideas would be as clear as possible to him. But you are right, there are futher optimizations if you need time critical code, as you have described below. But for this sort of batch processing thing it's not really necessary. More thoughts: <begin stable numbering> 1: one job I had I went home at the end of the, just left stuff running overnight. No worries. Unless it went wrong! :( 2: I reduced the time for my apx. 2000 paragraph test document by a couple orders of magnitude. That's the point where I give up. But as you say, it's still good fun! 3: Don't make it so fast that there's no time left to make a pretty status bar move across the screen to impress the boss ;) 4: My version seemed fast, but waiting for Pierre to test on /real/ documents. That's the acid test. <end stable numbering> Regards, Antony ~~~~~~~~~~ > > Would it be quicker to use para.range.start.insertbefore and > > (para.range.end - 1).insert.after to perform the insertion directly > without > > having to move the range? Or does that effectively re-evaluate the range > > anyway? > This is a more fruitful line of attack. However, better still might be to > use this to insert at the end of the para > .Characters.Last.InsertBefore Text:=("</" & my_array_of_styles(i)(MYTAG) & > ">") > This works because .Characters.Last is a range marking the last character of > the paragraph i.e. the paragraph mark. Use InsertBefore to insert the tag in > front of it. > One other speedup approach might be worth trying. Using a For Each loop to > go through one of Word's built in collections can be extremely slow for > larger collections. It may be faster to use the paragraphs.next property to > get the location of the next paragraph on from the current one, and then use > the InRange method to see whether the next paragraph is still inside the > selection. The outer loop would therefore become a Do While loop rather than > a For Each Next loop. > If there are more than a couple of hundred paragraphs in the selection you > should see a noticeable difference. If there are more than a thousand, you > should see a difference sufficient to transform the performance of the > macro. > There is one problem however with this approach. If the selection contains a > table with vertically merged cells, then the next paragraph within the table > can take you back to a cell that has already been checked. You need to have > code that guards against this. Its simple enough to do, you just check the > value of .Range.End aganst that of the previous paragraph. If it is less, > you need to take alternative action to move on. > > I love this stuff :-) > <aol>Me too!</aol> > -- > Regards > Jonathan West - Word MVP > MultiLinker - Automated generation of hyperlinks in Word > Conversion to PDF & HTML > http://www.multilinker.com > Word FAQs at http://www.multilinker.com/wordfaq > Please post any follow-up in the newsgroup. I do not reply to Word questions > by email
|
Mon, 10 Jan 2005 06:27:47 GMT |
|
|
Antony Scrive #12 / 21
|
WORD X: VBA macro including find/replace with styles much slower than regular macro
Eeep! Knew I was mailing from a different account, but didn't know I had *that* for my name and e-mail! Antony ~~~~~~~~~~ Quote:
> Hello all, > (Still the same me!) > ----- Original Message -----
> Newsgroups:
microsoft.public.mac.office.word,microsoft.public.word.vba.customization,mi c Quote: > rosoft.public.word.vba.general > Sent: Wednesday, July 24, 2002 3:33 PM > Subject: Re: WORD X: VBA macro including find/replace with styles muchslower > than regular macro
> > > Hi Anthony: > > > Lovely... Eloquent :-) > > > Now *I* have a question: Would we squeeze a few more CPU cycles out of > it > > if > > > we moved the evaluation of Ubound(my-array-of-styles) out of the loop? > Or > > > you have chosen because it's a very short array that the overhead's not > > > significant? > > I suspect that this will make little difference. It is a traditional code > > optimisation technique, but with Word VBA macros, the commands that > > manipulate numbers and strings are already lightning fast compared to > those > > which manipulate the Word object model. Even as it is, the UBound function > > is only evaluated once per paragraph, on first entry to the inner loop. > Agreed. Things like ubound() don't cause bottlenecks unless they > are being called time and time again in a tight loop. I don't think it's > really getting hammered in this instance. > (On the other hand I unwittingly got hammered by the lookup functions > when I first start programming with Access; they seemed so convenient... > So sometimes it does pay to investigate this sort of thing.) > But anyway, seeing as your definition of the array is hardcoded, > you could have the upper bound of the array as a constant, also > hard coded. But I don't like magic numbers in code (that's why > I defined the array the way I did, almost out of petulance!). Once > you've got the basic algorithm sorted out, all other optimization is a > case of diminishing returns at the expense of readability and > maintainabilty. Any optimizations should be carefully commented. > Plus I wanted to leave as much of Pierre's original code intact so > that my ideas would be as clear as possible to him. But you are right, > there are futher optimizations if you need time critical code, as you have > described below. But for this sort of batch processing thing it's not really > necessary. > More thoughts: > <begin stable numbering> > 1: one job I had I went home at the end of the, just left stuff running > overnight. No worries. Unless it went wrong! :( > 2: I reduced the time for my apx. 2000 paragraph test > document by a couple orders of magnitude. That's the > point where I give up. But as you say, it's still good fun! > 3: Don't make it so fast that there's no time left to make a pretty > status bar move across the screen to impress the boss ;) > 4: My version seemed fast, but waiting for Pierre to test > on /real/ documents. That's the acid test. > <end stable numbering> > Regards, > Antony > ~~~~~~~~~~ > > > Would it be quicker to use para.range.start.insertbefore and > > > (para.range.end - 1).insert.after to perform the insertion directly > > without > > > having to move the range? Or does that effectively re-evaluate the > range > > > anyway? > > This is a more fruitful line of attack. However, better still might be to > > use this to insert at the end of the para > > .Characters.Last.InsertBefore Text:=("</" &
my_array_of_styles(i)(MYTAG) Quote: > & > > ">") > > This works because .Characters.Last is a range marking the last character > of > > the paragraph i.e. the paragraph mark. Use InsertBefore to insert the tag > in > > front of it. > > One other speedup approach might be worth trying. Using a For Each loop to > > go through one of Word's built in collections can be extremely slow for > > larger collections. It may be faster to use the paragraphs.next property > to > > get the location of the next paragraph on from the current one, and then > use > > the InRange method to see whether the next paragraph is still inside the > > selection. The outer loop would therefore become a Do While loop rather > than > > a For Each Next loop. > > If there are more than a couple of hundred paragraphs in the selection you > > should see a noticeable difference. If there are more than a thousand, you > > should see a difference sufficient to transform the performance of the > > macro. > > There is one problem however with this approach. If the selection contains > a > > table with vertically merged cells, then the next paragraph within the > table > > can take you back to a cell that has already been checked. You need to > have > > code that guards against this. Its simple enough to do, you just check the > > value of .Range.End aganst that of the previous paragraph. If it is less, > > you need to take alternative action to move on. > > > I love this stuff :-) > > <aol>Me too!</aol> > > -- > > Regards > > Jonathan West - Word MVP > > MultiLinker - Automated generation of hyperlinks in Word > > Conversion to PDF & HTML > > http://www.multilinker.com > > Word FAQs at http://www.multilinker.com/wordfaq > > Please post any follow-up in the newsgroup. I do not reply to Word > questions > > by email
|
Mon, 10 Jan 2005 06:31:37 GMT |
|
|
Page 1 of 2
|
[ 21 post ] |
|
Go to page:
[1]
[2] |
|