Mittwoch, 18. Juni 2008

Zeichenketten auf ihren Aufbau prüfen / Checking strings on their structure

Im UT kam vor einigen Wochen die Frage auf, wie eine gültige Telefonnummer innerhalb einer Zeichenkette gefunden werden könne.

Der Aufbau der Zeichenketten war wie folgt:  
Suite 100 123-1234 -> 123-1234  
Room 4711 333-12345 -> ungültig  
Bldg J (910)222-2222 -> 222-2222

Die Lösung ist recht einfach. Im ersten Schritt wird die potentielle Telefonnummer (letzter Block) herausgelöst. Im zweiten Schritt erfolgt die Konvertierung dieses Strings in eine Maske, die im Anschluss direkt mit der gültigen Struktur (NNN-NNNN) verglichen wird.

LOCAL laZKette(3) as String, lcConvertToN as String, lcDelimiters as String, ;
  lcNumBlock as String, lcConverted as String

laZKette(1)     = [Bldg J (910)222-2222]
laZKette(2)     = [Room 4711 333-12345]
laZKette(3)     = [Suite 100 123-1234]
lcConvertToN    = [0123456789]
lcDelimiters    = [ )]
lcValidStruct   = [NNN-NNNN]
STORE [] TO lcNumBlock, lcConverted
CLEAR

FOR i = 1 TO ALEN(laZKette,1)

lcNumBlock    = GETWORDNUM(laZKette(i),GETWORDCOUNT(laZKette(i),lcDelimiters),lcDelimiters)
lcConverted    = CHRTRAN(lcNumBlock,lcConvertToN,REPLICATE([N],LEN(lcConvertToN)))

IF lcConverted == lcValidStruct
    ? [TelNr.: ] + lcNumblock + [ ist gültig]
ELSE
    ? [TelNr.: ] + lcNumblock + [ ist ungültig]
ENDIF

ENDFOR

In einigen Fällen kann es jedoch notwendig sein, eine einzelne Zeichenkette mit mehreren Formaten zu Vergleichen, um diejenige Formatversion zu liefern, die dem Zeichenkettenaufbau entspricht. Im folgenden Beispiel vergleichen wir drei gültige Formate mit 10 Werten.

Die in der Schleife eingesetzten CHRTRAN()-Funktionen konvertieren im ersten Schritt alle Buchstaben nach 'C' und im zweiten Schritt alle Zahlen nach 'N'. Diese Reihenfolge wurde gewählt, damit das 'N' nicht nach 'C' gewandelt wird, was bei umgekehrter Reihenfolge der Fall gewesen wäre. Natürlich hätten wir anstelle von 'N' auch die '9' oder das Nummernzeichen '#' zur Maskierung nehmen können. In diesem Fall wäre die Abarbeitungsreihenfolge irrelevant.

LOCAL    laZKette(10) as String, laStruct(3) as String ;
     lcConvertToN as String. lcConvertToC as String, ;
     lcConverted as String, liPos as Integer, lcMessage as String

laZKette( 1)  = [3UUFGP7]
laZKette( 2)  = [MCK000989]
laZKette( 3)  = [0090892LLG]
laZKette( 4)  = [9ABCD2]
laZKette( 5)  = [JKL230945]
laZKette( 6)  = [JKLM00989]
laZKette( 7)  = [JJ2000989]
laZKette( 8)  = [3U99G7]
laZKette( 9)  = [3UUFGP]
laZKette(10)  = [7XYZA1]

laStruct(1)  = [NCCCCCN]
laStruct(2)  = [CCCNNNNNN]
laStruct(3)  = [NNNNNNNCCC]

lcConvertToN = [0123456789]
lcConvertToC = [ABCDEFGHIJKLMNOPQRSTUVWXYZ]

STORE [] TO lcConverted, lcMessage
CLEAR

FOR i = 1 TO ALEN(laZKette,1)

 lcConverted = CHRTRAN(UPPER(laZKette[i]),lcConvertToC,REPLICATE([C],LEN(lcConvertToC)))
 lcConverted = CHRTRAN(UPPER(lcConverted),lcConvertToN,REPLICATE([N],LEN(lcConvertToN)))
 liPos       = ASCAN(laStruct,lcConverted,1,0,1,1+2+4)

 IF liPos > 0
     TEXT TO lcMessage NOSHOW TEXTMERGE PRETEXT 1+2
         Konto <<PADR(laZKette(i),11,[ ])>> = Struktur <<liPos>> (<<laStruct(liPos)>>)
     ENDTEXT
 ELSE
     TEXT TO lcMessage NOSHOW TEXTMERGE PRETEXT 1+2
         Konto <<PADR(laZKette(i),11,[ ])>> - ungültig -
     ENDTEXT
 ENDIF
 ?lcMessage

ENDFOR

Eine Erweiterung auf komplexere Konstrukte ist ebenfalls denkbar. So könnten Gross- und Kleinbuchstaben und auch Sonderzeichen in der Konvertierung Berücksichtigung finden.

2 Kommentare:

  1. Ich bin ja nu auch einer, der soweit wie möglich alles in VFP erschlagen möcht, aber für so ne Aufgabe ist nun mal der Einsatz von "Regulären Ausdrücken" die deutlich bessere, flexiblere, weniger aufwändigere Variante.

    Zur Ansteuerung der RegEx kann man im ersten Versuch die Klasse aus den FFCs verwenden, Suchmuster zur TelefonNummer-Erkennung findet man zuhauf im Internet (zb hier: http://regexlib.com/DisplayPatterns.aspx?cattabindex=6&categoryId=7)

    wOOdy

    AntwortenLöschen
  2. Hi Woody,

    Reguläre Ausdrücke sind eine feine Sache. Zu meinen Unix-Zeiten habe ich awk und sed diesbezgl. bis zum Exzess genutzt. Damit man mir also nicht nachsagt, das ich als einziges Werkzeug einen Hammer benutze und deswegen jedes Problem als Nagel ansehe hier nun das von Dir angesprochene Äquivalent über _regexp.vcx... ;-)

    SET CLASSLIB TO HOME()+ [ffc\_regexp.vcx]
    oexp = CREATEOBJECT([_regexp])
    oexp.clear
    oexp.pattern = "\d{3}-\d{4}$"
    * Liefert 1
    ?oexp.execute("Suite 100 123-1234")
    * Liefert 0
    ?oexp.execute("Room 4711 333-12345")
    * Liefert 1
    ?oexp.execute("Bldg J (910)222-2222")

    AntwortenLöschen