vbamacosms-word

MS Word Macro to Capitalizing the First Letter of each word in a document on a Mac Everything I try "Crashes Word"


I’m trying to capitalize the first letter of each word in a document and leave the rest of the word in camelCase.

So as an example:

wordExample

becomes

WordExample

I’ve tried many different variations of the following code (this is the simplest one), but every one of them crashes my MS Word instance on my Mac. I'm running Word 16.87 and macOS is 14.5 Any help will be greatly appreciated. Thanks in advance. - CES

Sub CapitalizeFirstLetterOnly()
 
    Dim doc As Document
    Dim word As Range
    Dim firstLetter As String
    Dim restOfWord As String
 
    Set doc = ActiveDocument
 
    For Each word In doc.words
 
        firstLetter = UCase(Left(word.Text, 1))
        restOfWord = Mid(word.Text, 2)
 
        word.Text = firstLetter & restOfWord
 
    Next word
   
End Sub

Solution

  • I ran this on Word 16.87 on MacOS 14.5 on an M1 Mac mini.

    The first point is that altering something in a collection you are iterating via "For Each" can cause an infinite loop, which is what is happening here (a single word can hang Word).

    One traditional solution to this is to iterate backwards, but because there is no For Each Backwards..Next loop (if only!) you have to do something more like

    Dim word As Long
    For word = doc.Words.Count to 1 Step -1
      ' process doc.Words(word)
    Next
    

    There are other things you can do to speed things up or avoid problems, e.g. put a DoEvents statement in your loop (maybe only run it every few hundred/thousand iterations) turn off ScreenUpdating during processing ... and doubtless others.

    But as far as I can tell, the main problem here is that accessing the Words collection is painfully slow. It's not just a Mac thing - it's even worse on my Windows box. Further, accessing words nearer the end of the document is much slower than accessing them the beginning. e.g., it took 90 seconds here to process around 5000 words. If you do a single-letter Find/Replace, on the same text, it's almost instantaneous. (So a sequence of find/replace operations might be one way to go).

    There is a Question in this general area here with an answer that may be useful, and there may be others. But IMO if your Words are grouped into paragraphs, processing will likely be far faster if you process paragraphs, then words within paragraphs.

    e.g. for testing I ended up using this:

    Sub CapitalizeFirstLetterOnly()
     
        Dim doc As Document
        Dim para As Long
        Dim word As Long ' note type change from Word
        Dim firstLetter As String
        Dim restOfWord As String
        Dim st As Date
        st = Time ' remember the start time
        Set doc = ActiveDocument
        On Error GoTo finish
        Application.ScreenUpdating = False
        For para = doc.Paragraphs.Count To 1 Step -1
           With doc.Paragraphs(para).Range.Words
               For word = .Count To 1 Step -1
        
                   firstLetter = UCase(Left(.Item(word).Text, 1))
                   restOfWord = Mid(.Item(word).Text, 2)
     
                   .Item(word).Text = firstLetter & restOfWord
        
                   If (para Mod 20) = 0 Then
                       Debug.Print para
                       DoEvents
                   End If
               Next word
            End With
        Next
    finish:
        Application.ScreenUpdating = True
        Set doc = Nothing ' don't forget this!
        Debug.Print st, Time
    End Sub