Auslesen von deutschen Empfängeradressen mit Kofax Transformation Modules (KTM)

Keine Kommentare

auslesen empfängeradressen anschreiben

Das Auslesen von Adress-/Anschriftbereichen in Briefen war schon immer eine recht schwierige Problematik. Die Freude war umso größer, als Kofax vor einigen KTM-Versionen (Kofax Transformation Modules) ein Werkzeug (Adress-Lokator) für das automatisierte Auslesen von Adressen bereitstellte. Leider währte die Freude darüber nicht lange, denn dieser neue Lokator erkannte nur amerikanische Adressen. Er ist nur eine Blackbox und nicht konfigurierbar (abgesehen von Regionszonen). Da sich in den neueren KTM-Versionen daran nichts änderte und ein Kunde mal wieder Bedarf nach dem Auslesen des Anschriftbereichs äußerte, habe ich nun dafür eine einfache ‚Zu-Fuß‘-Lösung entwickelt. Damit sollte ein großer Teil der deutschen Anschreiben erkannt werden.

Die erste Idee war, einen Datenbanklokator mit einer Fuzzy DB zu nutzen. Grundlage dafür war eine CSV-Datei, die die Straßen mit Hausnummern, PLZ und Ort enthält. Solche Dateien kann man käuflich erwerben oder sich mit etwas Aufwand selbst zusammenstellen:
:
Schwedenloch;1;94250;Achslach
Dorfplatz;1;94250;Achslach
Bahnhofstraße;2;85111;Adelschlag
Am Bründl;3;85111;Adelschlag
Am Bründl;9;85111;Adelschlag
Am Bründl;7;85111;Adelschlag
:

Leider waren die Testergebnisse nicht so gut wie erhofft. Das lag einerseits am doch unvollständigen und nicht immer korrekten Datenmaterial, aber auch den Eigenheiten der Suche mit der Fuzzy DB. Als nächstes kam eine Fuzzy DB nur mit den PLZ und Orten zum Einsatz. Aber auch damit konnten keine zufriedenstellenden Ergebnisse erzielt werden.

Das Ziel beider Ansätze war, die Zeile mit dem Straßennamen bzw. die Zeile mit PLZ/Ort mit sehr hoher Konfidenz zu finden. Ausgehend von dieser Zeile kann man dann mit etwas KTM-Skripting die restlichen Adressbestandteile „zusammensuchen“.

Schlussendlich wurde dann der einfache, aber deterministische Ansatz mit einem einfachen Formatlokator gewählt, um die Zeile mit PLZ/Ort eindeutig zu identifizieren. Dabei wäre es naheliegend, im Formatlokator Wörterbücher mit PLZ bzw. Orten einzusetzen:

:
01067,Dresden,,
01069,Dresden,Innere Altstadt,
01097,Dresden,,
01099,Dresden,,
:

Aber auch dabei fielen wieder Fehler im Datenbestand auf.

Daher wurde nur ein Wörterbuch mit den Ortsnamen genutzt. Die PLZ wurde stattdessen durch einen regulären Ausdruck dargestellt. Das hatte den weiteren Vorteil, dass auch alle PLZ von Postfächern gefunden wurden. Der Formatlokator sucht also alle fünfstelligen Zahlen links vor einem deutschen  Städtnamen in der Region, in der normalerweise die Empfängeradresse steht. Hier der eingesetzte, einfache Formatlokator:
Formlokator

Formlokator

Bem.: „Staedte“ ist ein KTM-Wörterbuch, das aus einer Liste aller deutschen Städtenamen besteht (siehe unten).

staedte Wörterbuch
Der Test findet zwei mögliche Treffer:

42697 (Solingen) und

12345 (Dortstadt)

Hier noch die Definition des Wörterbuchs „Staedte“:

Definition Staedte

Mit den Ergebnissen des Formatlokators werden nun die restlichen Adressbestandteile per KTM-Skript bestimmt. Der Einfachheit halber wird nach einer Zeile mit PLZ/Ort, einer Zeile mit Straße/Hausnummer bzw. Postfach und nach maximal zwei Namenszeilen gesucht (Leerzeilen ausgenommen).

Grundsätzlich wurde dabei folgendes Vorgehen implementiert:

Von allen Alternativen des Formatlokators mit einer Konfidenz >.99 wird die „unterste“ genommen, um einen eventuellen Treffer in einer Absenderzeile zu vermeiden. Die gefundenen Alternativen legt die Zeilenummer der Zeile mit PLZ/Ort fest. Aus dieser Zeile können dann PLZ und Ort bestimmt werden (Details: siehe Skripting unten).

Die Straße/Postfach sollte in der darüberliegenden Zeile stehen. Dabei müssen einerseits Leerzeilen ignoriert werden, aber auch Zeilen, die nur „rechts“ in der Zeile Text beinhalten. Das kann über die Eigenschaft „left“ im Skript festgestellt werden. Sobald eine passende Zeile gefunden wurde, kann dann die Straße bzw. das Postfach bestimmt werden.

Ein ähnliches Vorgehen liefert dann noch bis zu zwei Namenszeilen.

Das Skripting im KTM-Projekt wurde im Document_AfterExtract-Event der Dokumentklasse untergebracht. Aber man kann es natürlich als Skriptlokator implementieren.

' ' Class script: Doks
Private Sub Document_AfterExtract(ByVal pXDoc As CASCADELib.CscXDocument)

Dim anztreffer As Integer
Dim i As Integer
Dim PLZ As String
Dim zeilennummer As Integer
Dim zeile As String
Dim Ort As String
Dim j As Integer
Dim tmpZeile As String
Dim Name1 As String
Dim Name2 As String
Dim sPattern As String

'***************************************************************
'PLZ und Ort finden
'alle Alternativen des Lokators 'Orte' durchgegehen, die eine Konfifdenz >.99 haben und die letzte (unterste) davon nehmen
'dadurch wird verhindert, dass eine eventuell vorhandene Absenderzeile oberhalb der Empfängeradresse genommen wird
anztreffer=pXDoc.Locators.ItemByName("Orte").Alternatives.Count 'Anzahl Alternativen des Lokators
If anztreffer>0 Then
   For i= 0 To anztreffer-1 'Schleife über alle Alternativen
      If pXDoc.Locators.ItemByName("Orte").Alternatives(i).Confidence>.99 Then 'nur 100% nehmen
         PLZ=pXDoc.Locators.ItemByName("Orte").Alternatives(i).Text 'die PLZ ist der Wert der aktuellen Alternative
         'Bestimmen der Zeilennummer der aktuellen Alternative
         zeilennummer=pXDoc.Locators.ItemByName("Orte").Alternatives(i).Words.ItemByIndex(0).LineIndex
         zeile=pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(zeilennummer).Text 'Die komplette Textzeile der Alternative
            'Der Ort steht rechts von der PLZ
            'Es könnte auch noch Text von der rechten Seite enthalten sein
            'Nur Words mit left<1100 nehmen.
            Ort=""
            For j= 0 To pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(zeilennummer).Words.Count-1 'nur words mit left<1100 nehmen
               If pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(zeilennummer).Words(j).Left<1100 Then
                  If pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(zeilennummer).Words(j).Text<>PLZ Then
                     Ort=Ort+pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(zeilennummer).Words(j).Text+" "
                  End If
               End If
            Next
            Ort=Trim(Ort)
      End If
   Next
   pXDoc.Fields.ItemByName("PLZ").Text=PLZ 'Ergebnis in die Felder stellen
   pXDoc.Fields.ItemByName("Ort").Text=Ort

   '***************************************************************
   'Oberhalb von PLZ/Ort sollte die Strasse stehen (oder Leerzeilen)
   'Nur die ersten 40 Zeichen berücksichtigen, da weiter rechts noch anderer Text stehen kann.
   'Die ganze Adresse soll nur aus maximal 5 Zeilen bestehen
   If zeilennummer>0 Then 'PLZ/Ort wurden oben gefunden
      tmpZeile=""
      i=zeilennummer 'das ist die Zeile mit PLZ/Ort
      Do While tmpZeile="" 'ausgehend von der PLZ/Ort-Zeile aufwärts nach der Strassenzeile suchen
         i=i-1
         'Nur die ersten 40 Zeichen berücksichtigen, da weiter rechts noch anderer Text stehen kann.
         tmpZeile=Trim(Left(pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i).Text,40))
         'Es kann sein, dass der Text von der rechten Seite kommt, weil die Adresse hier eine Leerzeile hat.
         'Alles was left>1100 ist von der rechten Seite und kann weg
         If pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i).Left>1100 Then
            tmpZeile=""
         End If
         'irgendwann muss Schluß sein. Bei Adressen mit sehr vielen ggf. hier anpassen
         If zeilennummer-i=5 Then Exit Do
      Loop
      'in der Strassenzeile alle Worte mit Left>=1100 entfernen, da sie von der rechten Bildseite stammen
      ' i ist Index der Strassenzeile
      tmpZeile=""
      For j= 0 To pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i).Words.Count-1 'nur words mit left<1100 nehmen
         If pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i).Words(j).Left<1100 Then
            tmpZeile=tmpZeile+pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i).Words(j).Text+" "
         End If
      Next
      tmpZeile=Trim(tmpZeile)
      pXDoc.Fields.ItemByName("Strasse").Text=tmpZeile 'Ergebnis in Feld stellen


      '***************************************************************
      'jetzt noch maximal zwei Zeilen aufwärts für Name/Abtlg. o.ä.
      Name2=""
      For j= 0 To pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i-1).Words.Count-1
         'nur words mit left<1100 nehmen, damit nichts von der rechten Seite genommen wird
         If pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i-1).Words(j).Left<1100 Then
            Name2=Name2+pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i-1).Words(j).Text+" "
         End If
      Next
      Name1=""
      For j= 0 To pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i-2).Words.Count-1
         'nur words mit left<1100 nehmen, damit nichts von der rechten Seite genommen wird
         If pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i-2).Words(j).Left<1100 Then
            Name1=Name1+pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i-2).Words(j).Text+" "
         End If
      Next
      If Name1="" Then 'es gab nochmal eine Leerzeile
         For j= 0 To pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i-3).Words.Count-1
            'nur words mit left<1100 nehmen, damit nichts von der rechten Seite genommen wird
            If pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i-3).Words(j).Left<1100 Then
               Name1=Name1+pXDoc.Pages.ItemByIndex(0).TextLines.ItemByIndex(i-3).Words(j).Text+" "
            End If
         Next
      End If
      'falls Name1 eine PLZ (5 Ziffern) enthält, ist es die Absenderzeile - ignorieren
      sPattern="\d{5}"
      'Funktion zur Suche mit regulären Ausdrücken (siehe Skript auf Projektebene)
      If RegExprFound(Name1,sPattern)=True Then
         Name1=""
      End If
      pXDoc.Fields.ItemByName("Namenszeile1").Text=Name1'Ergebnis in Feld stellen
      pXDoc.Fields.ItemByName("Namenszeile2").Text=Name2
   End If

End If
End Sub


'***************************************************************************
' Mit dieser Funktion kann mit Regulären Ausdrücken gesucht werden
' Als Referenz ist "Microsoft VBScript Regular Expressions 5.5" einzubinden
'
Public Function RegExprFound(sText As String, sPattern As String) As Boolean
   Dim oRegEx As New RegExp
   Dim oMatches As MatchCollection

   oRegEx.Pattern = sPattern
   Set oMatches = oRegEx.Execute(sText)
   If oMatches.Count = 1 Then
      RegExprFound=True
   Else
      RegExprFound=False
   End If
End Function

Hier die Ergebnisse mit unterschiedlich aufgebauten Briefköpfen (Adresse und rechte Seite):

Ergebnisse Auslesen Empfängeradressen

Ergebnisse Auslesen Empfängeradressen

Ergebnisse Auslesen Empfängeradressen

Ergebnisse Auslesen Empfängeradressen

Fazit

Der standardmäßig vorhandene Adress-Lokator von KTM liefert nur für amerikanische Adressen Ergebnisse. Versuche, deutsche Adressen mit Fuzzy-DB-Suche und verschiedenen Datenbeständen (PLZ, Orte, Strassen, Hausnummern) zu extrahieren, lieferten auch keine befriedigenden Resultate. Wir haben daher einen einfachen Formatlokator benutzt, um die Textzeile mit der PLZ und dem Ort zu bestimmen. Davon ausgehend konnten dann mit etwas KTM-Skripting die restlichen Adressbestandteile bestimmt werden.

Jürgen Voss unterstützt unsere Kunden bei der Erfassung von Eingangsdokumenten, deren Klassifikation samt Datenextraktion und anschließendem Starten der Geschäftsprozesse.

Die erfassten Dokumente werden dabei in Dokumentenmanagement-systemen wie CenterDevice oder Archivsystemen sicher abgelegt. Dazu zählt auch die E-Mail-Archivierung und die Ablage über die SAP-Archivierungsschnittstelle ArchiveLink.

Über 1.000 Abonnenten sind up to date!

Die neuesten Tipps, Tricks, Tools und Technologien.
Jede Woche direkt in deine Inbox.

Kostenfrei anmelden und immer auf dem neuesten Stand bleiben!
(Keine Sorge, du kannst dich jederzeit abmelden.)

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht.