WORD X: VBA macro including find/replace with styles much slower than regular macro 
Author Message
 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:="&amp;"
>      characterSwitch myOld:="^s", myNew:="&nbsp;"
>   characterSwitch myOld:="(c)", myNew:="&copy;"
>     characterSwitch myOld:="(tm)", myNew:="&tm;"
>     characterSwitch myOld:="(r)", myNew:="&reg;"
> 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  
 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:="&amp;"
> >      characterSwitch myOld:="^s", myNew:="&nbsp;"
> >   characterSwitch myOld:="(c)", myNew:="&copy;"
> >     characterSwitch myOld:="(tm)", myNew:="&tm;"
> >     characterSwitch myOld:="(r)", myNew:="&reg;"
> > 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  
 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  
 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  
 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:="&amp;"
> >      characterSwitch myOld:="^s", myNew:="&nbsp;"
> >   characterSwitch myOld:="(c)", myNew:="&copy;"
> >     characterSwitch myOld:="(tm)", myNew:="&tm;"
> >     characterSwitch myOld:="(r)", myNew:="&reg;"
> > 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  
 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,micro
> 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:="&amp;"
>>>      characterSwitch myOld:="^s", myNew:="&nbsp;"
>>>   characterSwitch myOld:="(c)", myNew:="&copy;"
>>>     characterSwitch myOld:="(tm)", myNew:="&tm;"
>>>     characterSwitch myOld:="(r)", myNew:="&reg;"
>>> 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  
 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  
 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  
 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:

> 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



Sun, 09 Jan 2005 22:33:18 GMT  
 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  
 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  
 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,mic
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)

- Show quoted text -

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  
 
 [ 21 post ]  Go to page: [1] [2]

 Relevant Pages 

1. Find and Replace in Word Document using VBA

2. Find and Replace in regular text box?

3. Word Templates with VBA code running very slow in Word XP

4. VBA in a workgroup template runs slower on Word 2002 than on Word 97

5. Finding Word 6.0/95 files and replacing them in Word 2000 format

6. find/replace in a Word Doc using word object and vbscript

7. Replacing styles doesn't set .Found=True

8. find and replace all styles

9. Word Macro runs then gets Macro Not Found

10. Word Macro runs then gets Macro Not Found

11. Word 97 - Userform within Word VBA macro and exchange of values

12. Replacing all paragraph marks in a certain style EXCEPT the last one in this style

 

 
Powered by phpBB® Forum Software