smart software solutions


Suche mit Wildcards

Vorbemerkung

Die Suche mit Wildcards (wildcard search) ist vor allem aus dem Dateisystem geläufig. '?' steht für ein einzelnes Zeichen, '*' für eine beliebige Zeichenkombination (oder kein Zeichen). Häufig werden hier Dateinamen gesucht z.B. T*.doc, das alle Dokumente mit T am Anfang findet. Leider sucht man eine funktionierende Stringsuche nach diesen Regeln in Delphi vergeblich und auch die Sources, die ich so im Netz fand, hatten so ihre Macken.



Suche mit Wildcard
Aufruf: WPos(Wildcard,Text [,StartPos][,'*'][,'?'])
Rückgabe:tposition: .start, .len

wpos() ist pos() mit Wildcards: ? für ein beliebiges Zeichen, * für 0-n beliebige Zeichen. Die Syntax orientiert sich am pos-Befehl, der einfachste Aufruf ist   wpos(wild,s). Wpos findet das erste Auftreten des Suchstrings Wild. Da die Länge größer sein kann als die des Suchstrings muß auch die Länge der gefundenen Passage zurückgegeben werden. Daher wird das Ergebnis in einem TPosition-Record zurückgegeben. Den Start des gefundenen Strings erhält man z.B. mit wpos().start

Das Isolieren des gefundenen Strings kann mit folgender Sequenz erfolgen
    with wpos('?x','Text') do
        s := copy('Text',start,len);
Durch Angabe der Startposition eignet sich wpos auch zum Parsen. Für Sonderfälle können die Wildcards durch beliebige Zeichen ausgetauscht werden.

Hier nun der vollständige Code
Type
 TPosition = record // Position eines Substrings in einem String
    start,len: integer;
 end;

{ -------------------------------------------------
  Wildcard-Compare von Dr. Ecki
  mit Rückgabe gefundene Position
  -------------------------------------------------}
function WPos(const Wild,S: string; const StartPos: Integer = 1;
                    const all: char='*'; const one: char='?'): TPosition;
var lw,ls: integer;
    cw: char;

    //-- match ab einer bestimmten Position ps, Result wird als res übergeben,
                                                Rückgabe gefunden: ja/nein --//

    function match(ps: integer; var res: TPosition): boolean;
    var i: integer;           
begin result := False; res.start := ps; // Anfang schon mal merken for i := 1 to lw do begin if ps>ls then // Falls Schleife noch läuft und String schon // zuende, dann Fehler exit; cw := wild[i]; if cw = one then // Aktueller Wild-char inc(ps) // ?: egal, nur hochzählen else if cw = all then begin if i=lw then // *: Wenn es das letzte Zeichen ist, dann muß // man nichts tun! ps := ls+1 // Dann bis zum Ende else begin if wild[i+1]<>all then // Wenn danach noch ein * kommt, dann auch nicht begin if wild[i+1]=one then raise exception.Create('illegal *? in wpos'); ps := charpos(s,wild[i+1],ps); // sonst zum nächsten Zeichen // springen end; end; end else // normales Zeichen im Wildstring if cw<>s[ps] then exit else inc(ps); end; res.len := ps-res.start; result := True; end; // main var ps: integer; label nomatch; begin ls := length(s); lw := length(wild); //------ Abfangen diverser Sonderfälle ------ if (lw=0) or (ls=0) then goto nomatch; cw := wild[1]; if lw=1 then // nur ein Sonderzeichen abfangen begin if (cw=all) or ((cw=one) and (ls=1))then begin result.start:=1; result.len := ls; exit; end; if (cw=one) then goto nomatch end; ps := StartPos; // nsp = NextSearchPosition //------ Hauptschleife ------ repeat cw := wild[1]; if cw=one then // Sonderfall erstes Suchzeichen ? begin if (wild[2]=all) or (wild[2]=one) then raise exception.Create('illegal *? in wpos'); ps := charpos(s,wild[2],ps+1)-1; end else if cw<> all then ps := charpos(s,cw,ps); // nächstes Auftauchen von wild[1 suchen] if ps=0 then // 0 = nicht gefunden goto nomatch; // ================= Aufruf von Match immer ab ps ==================== if match(ps,result) then // Wenn gefunden, dann abbrechen exit; // --> // =================================================================== inc(ps); until ps>=ls; nomatch: result.start := 0; result.len := 0; end;