Blog | Beitrag

Word-Makro: Text durch Hyperlinks in allen Dokumentbereichen ersetzen

Im Rahmen meiner Tätigkeit für den parlamentarischen Untersuchungsausschuss zur Untersuchung der politischen Verantwortung im Zusammenhang mit dem Kampfflugzeugsystem “Eurofighter Typhoon” von Anfang 2000 bis Ende 2017 stellte sich das Problem, Dokumentenbezeichnungen in Word-Dateien durch Hyperlinks, welche zu diesen Dokumenten in einer zentralen Datenbank verlinken sollten, zu ersetzen.
Eine erste Hürde bestand darin, dass diese Dokumentenbezeichnungen in allen Bereichen der Word-Dateien vorkommen konnten, daher die Dokumentenbezeichnungen konnten im normalen Fließtext verwendet werden und/oder in Fuß-/Endnoten als Zitat (dies war überwiegend der Fall).
Ein weiteres Problem bestand darin, die Dokumentenbezeichnungen automatisiert im Dokument ausfindig zu machen. Die Struktur der Bezeichnung war stets DokNr gefolgt von (idealerweise einem) Leerzeichen und einer fünfstelligen Zahl (z.B. DokNr 12345). Zu beachten war hierbei, dass die einzelnen Dokumentenbezeichnungen sich unterscheiden, aber auch öfters/wiederholt vorkommen konnten (z.B. wenn ein Dokument mehrfach zitiert wurde).

Mein erster Fehlversuch basierte auf RegExp (war meiner Affinität für PHP/JS-Code geschuldet wo ich gerne und recht häufig mit preg_match und preg_replace arbeite) und sah wie folgt aus:

Sub TextToHyperlink()
    Dim rngFT As Word.Range
    Dim matchFT As Word.Range
    Dim rngStory As Word.Range
    Dim objRegExp As Object
    For Each rngStory In ActiveDocument.StoryRanges
        Set rngFT = rngStory
        Set matchFT = rngStory
        Set objRegExp = CreateObject("VBScript.RegExp")
        With objRegExp
            .Global = True
            .IgnoreCase = False
            .Pattern = "(DokNr) ([0-9]+)"
        End With
        Set Matches = objRegExp.Execute(matchFT)
        For Each match In Matches
            With rngFT.Find
             .Forward = False
             .ClearFormatting
             .MatchWholeWord = True
             .MatchCase = False
             .Wrap = wdFindContinue
             .Execute FindText:=match.Value
            End With
            ActiveDocument.Hyperlinks.Add Anchor:=rngFT, _
                Address:="https://datenbank.domain.at/search/results.php?pardoknr" _
                & match.Submatches(1) & "&f=U", SubAddress:="", ScreenTip:="", _
            TextToDisplay:="DokNr " & match.Submatches(1)
        Next match
    Next rngStory
    Set rngFT = Nothing
    Set matchFT = Nothing
    Set rngStory = Nothing
    Set objRegExp = Nothing
End Sub

Visual Basic
Der Code schien zu funktionieren und schloss dank der “For Each”-Schleife durch ActiveDocument.StoryRanges auch alle Textbereiche (Kopfbereich, Fließtext, Fußnoten, etc.) mit ein, jedoch zeigte sich bald der “logische Fehler” meines Codes, wodurch Dokumentenbezeichnungen nur einmal durch Hyperlinks ersetzt wurden, auch wenn diese öfters im Text vorkamen. So wurde beispielsweise nur beim ersten Zitat die DokNr 12345 durch einen Hyperlink zum Dokument ersetzt, alle weiteren Vorkommnissen von DokNr 12345 wurden ignoriert und nicht wie intendiert durch einen Hyperlink ersetzt.
Ich überlasse es jetzt Dir als Leser den logischen Fehler zu finden – ein Tipp: den Inhalt der Variable “Matches” (ein Array) ansehen.
Nach ein wenig Recherche bin ich zu einer wesentlich einfacheren und eleganteren Lösung unter Einbeziehung von “wildcards” gekommen:

Sub TextToHyperlink()
    Application.ScreenUpdating = False
    Dim rngStory As Word.Range
    Dim matchFT As Word.Range
    For Each rngStory In ActiveDocument.StoryRanges
        Set matchFT = rngStory
        With matchFT
          With .Find
            .Text = "DokNr @[0-9]{5}"
            .Replacement.Text = ""
            .Forward = True
            .Wrap = wdFindStop
            .Format = False
            .MatchWildcards = True
            .Execute
          End With
          Do While .Find.Found
            TrimString = Trim(.Text)
            DokNr = Right(TrimString, 5)
            .Hyperlinks.Add Anchor:=.Duplicate, Address:="https://datenbank.domain.at/results.php?pardoknr" _
             & DokNr & "&f=U", TextToDisplay:=.Text
            .Start = .Hyperlinks(1).Range.End
            .Find.Execute
          Loop
        End With
    Next rngStory
    Set rngStory = Nothing
    Set matchFT = Nothing
    Application.ScreenUpdating = True
End Sub

Visual Basic
Kurze Analyse des Codes:
Die For-Each-Schleife

...
    For Each rngStory In ActiveDocument.StoryRanges
    ...
    Next rngStory
...

Visual Basic
stellt sicher, dass ALLE Textbereiche (Kopfbereich, Fließtext, Fußnoten, etc.) nach den Dokumentenbezeichnungen durchsucht werden.
Mit

...
          With .Find
            .Text = "DokNr @[0-9]{5}"
            .Replacement.Text = ""
            .Forward = True
            .Wrap = wdFindStop
            .Format = False
            .MatchWildcards = True
            .Execute
          End With
...

Visual Basic
werden alle Dokumentenbezeichnungen im jeweiligen Textbereich gesucht. Durch DokNr @ wird sichergestellt, dass die Dokumentenbezeichnungen auch dann als solche erkannt werden, wenn mehrere Leerzeichen zwischen DokNr und der fünfstelligen Dokumentennummer gesetzt wurden. Durch [0-9]{5} werden nur Zahlen zwischen 0-9 gefunden, wobei diese immer fünfstellig sein müssen.
Danach ersetzt dieser Code:

...
          Do While .Find.Found
            TrimString = Trim(.Text)
            DokNr = Right(TrimString, 5)
            .Hyperlinks.Add Anchor:=.Duplicate, Address:="https://datenbank.domain.at/results.php?pardoknr" _
             & DokNr & "&f=U", TextToDisplay:=.Text
            .Start = .Hyperlinks(1).Range.End
            .Find.Execute
          Loop
...

Visual Basic
die jeweils gefundenen Dokumentenbezeichnungen durch die zugehörigen Hyperlinks. Trim(.Text) sorgt dafür, dass etwaige Leerzeichen vor oder hinter der Dokumentenbezeichnung entfernt werden. Right(TrimString, 5) extrahiert dann die fünfstellige Dokumentennummer. Der folgende .Hyperlinks.Add-Code ersetzt anschließend die Dokumentenbezeichnung durch einen Hyperlink, wobei die Variable DokNr die fünfstellige Dokumentennummer in die URL des Hyperlinks einbaut. Durch TextToDisplay:=.Text wird sichergestellt, dass als Linktext wieder die (durch einen Hyperlink ersetzte) Dokumentenbezeichnung verwendet wird.

Diesen Beitrag teilen