Home | Chi sono | Contattami
 

Progr. lineare

Delphi
 
Componenti
  Database
 
Miei articoli

Windows

Miei articoli 

 

Espressioni Regolari in Delphi


E' possibile utilizzare le espressioni regolari in Delphi grazie alla loro implementazione all'interno di "Microsoft® Windows® Script". Anzitutto premetto che questo articolo non è una trattazione teorica delle espressioni regolari (per la quale rimando agli innumerevoli riferimenti su internet) ma una spiegazione di come poter utilizzare le espressioni regolari in Delphi.

1. Microsoft Windows Script

Per prima cosa è opportuno scaricare da internet l'ultima versione disponibile di "Microsoft® Windows® Script". Una volta scaricato, lanciamolo per effettuare l'installazione. Verrà installato Microsoft® Windows® Script che contiene: 

  • Visual Basic® Script Edition (VBScript.) Version 5.6
  • JScript® Version 5.6, Windows Script Components
  • Windows Script Host 5.6
  • Windows Script Runtime Version 5.6

Ciò che ci interessa è l'implementazione delle espressioni regolari, che si trova nel file "vbscript.dll". Tutte le volte che si vorrà utilizzare un programma che sfrutta le espressioni regolari utilizzando "Microsoft® Windows® Script" su un determinato computer, sarà necessario copiare tale file sul computer destinazione ed effettuarne la registrazione con la seguente linea di comando:

  regsvr32 vbscript.dll

E' da notare che il pacchetto autoinstallante scaricato da internet effettua già in automatico questa operazione.

2. Importazione in Delphi della libreria

Dal menù "Project" seleziona "Import type library": si apre una maschera contenente una lista. All'interno della lista selezionare 

  "Microsoft VBScript Regular Expressions"
 

(seguito da un numero di versione). E' possibile che vi sia più di un elemento con questo nome (cambia solo il numero di versione): selezionare in tal caso il valore con numero di versione più alto. Faremo riferimento alla versione

  "Microsoft VBScript Regular Expressions 5.5 (Version 5.5)"

In corrispondenza di questa versione ci sono i seguenti "Class Names"

  • TRegExp
  • TMatch
  • TMatchCollection
  • TSubMatches

Definire il nome della unit pascal che rappresenta l'importazione della type library, alla voce "Unit dir name". A questo punto si può decidere, tramite la casella di spunta "Generate Component Wrapper", di installare nella paletta dei componenti, alla voce specificata nella casella di testo "Palette Page", i "Class Names". Eliminando la spunta non verrà installato nulla. Procediamo eliminando l'eventuale spunta (e quindi eliminando l'installazione nella paletta dei componenti). Premendo il pulsante "Create Unit" si ha la creazione della unit di importazione.

Sono dichiarate le seguenti "interface":

  • IRegExp = interface;
  • IMatch = interface;
  • IMatchCollection = interface;
  • IRegExp2 = interface;
  • IMatch2 = interface;
  • IMatchCollection2 = interface;
  • ISubMatches = interface;

IRegExp e IRegExp2 sono versioni differenti (IRegExp2 è la più recente) della stessa "interface". Lo stesso discorso vale per le altre "interface".

Per garantire compatibilità tra le varie versioni sono definiti i seguenti tipi:

  • RegExp = IRegExp2;
  • Match = IMatch2;
  • MatchCollection = IMatchCollection2;
  • SubMatches = ISubMatches;

2.1 IRegExp2

IRegExp2 è la "interface" principale:

Proprietà

    a) property Pattern: WideString read Get_Pattern write Set_Pattern;
    //espressione regolare
    b) property IgnoreCase: WordBool read Get_IgnoreCase write Set_IgnoreCase;
    //ricerca "case insensitive" (TRUE o FALSE)  
    c) property Global: WordBool read Get_Global write Set_Global;
    //ricerca globale sulla stringa in input al metodo "Execute",
    //oppure la ricerca si deve bloccare alla prima corrispondenza
    //trovata (TRUE per ricerca globale, FALSE altrimenti)
    d) property Multiline: WordBool read Get_Multiline write Set_Multiline;
    //se la stringa, su cui devono essere ricercate le corrispondenze,
    //contiene dei caratteri di ritorno a capo, la stringa in input
    //è costituita da varie righe. Se Multiline = FALSE (valore di
    //default) allora le corrispondenze del pattern verranno cercate 
    //relativamente alle righe. Altrimenti (Multiline = TRUE) le
    //corrispondenze del pattern verranno ricercate relativamente
    //all'intera stringa. 

Metodi

    a) function  Execute(const sourceString: WideString): IDispatch; safecall;
    //effettua una ricerca, sulla stringa in input, di tutte le
    //stringhe (o di una sola se "Global = FALSE") che corrispondono
    //al pattern specificato in "Pattern".
    b) function  Test(const sourceString: WideString): WordBool; safecall;
    //restituisce TRUE se esiste almeno una corrispondenza del
    //pattern specificato nella stringa di input
    c) function  Replace(const sourceString: WideString; const replaceString: WideString):  WideString; safecall;
    //effettua la sostituzione di tutte le stringhe, all'interno di
    //"sourceString", che corrispondono al pattern specificato in
    //"Pattern", con la stringa definita da "replaceString". Si
    //possono usare i valori $1, $2, $3, ... per definire una stringa
    //di sostituzione costituita dalle sottostringhe definite dal pattern 

2.2 IMatchCollection2

IMatchCollection2 raccoglie tutte le corrispondenze col pattern specificato.

Ad esempio:

var   i: integer;   MatchesCollection: IMatchCollection2;  {...}    MatchesCollection := Execute(InputStr) as IMatchCollection2;   for i := 1 to MatchesCollection.Count - 1 do     begin       Memo1.Lines.Add((MatchesCollection.Item[i] as IMatch2).Value);     end; {...} 

Nota che, come detto in precedenza, posso sostituire i nomi delle interface con i seguenti tipi:

  • RegExp            (IRegExp2)
  • Match             (IMatch2)
  • MatchCollection   (IMatchCollection2)
  • SubMatches        (ISubMatches)

Le proprietà principali di IMatchCollection2 sono le seguenti: 

    a) property Item[index: Integer]: IDispatch read Get_Item; default;
    //corrispondenze col pattern specificato; i valori di index partono da 0
    b) property Count: Integer read Get_Count;
    //numero di corrispondenze col pattern specificato
 

I valori della proprietà Item sono di tipo IMatch2.

2.3 IMatch2

IMatch2 rappresenta le caratteristiche di ogni corrispondenza.

Le proprietà principali sono:

    a) property Value: WideString read Get_Value;
    //valore della stringa
    b) property FirstIndex: Integer read Get_FirstIndex;
    //posizione della stringa nella stringa in input 
    c) property Length: Integer read Get_Length;
    //lunghezza della stringa
    d) property SubMatches: IDispatch read Get_SubMatches;
    //sottostringhe della stringa ($1, $2, $3, ...)
 

2.4 ISubMatches

ISubMatches raccoglie i valori di $1, $2, $3, ...

Le proprietà principali sono:

    a) property Item[index: Integer]: OleVariant read Get_Item; default;
    //ad esempio: Item[3] è $3; i valori di index partono da 0
    b) property Count: Integer read Get_Count;
    //numero di sottostringhe
 

Breve digressione sulle variabili $1, $2, $3, ...:

Dato il pattern, sono identificabili nel seguente modo: si esaminano le parentesi aperte da sinistra verso destra:

$1 è la parte di stringa contenuta a partire dalla prima parentesi aperta fino alla parentesi chiusa corrispondente.  
$2 è la parte di stringa contenuta a partire dalla seconda parentesi aperta fino alla parentesi chiusa corrispondente.  
$3 è la parte di stringa contenuta a partire dalla terza parentesi aperta fino alla parentesi chiusa corrispondente.  

....

Ad esempio sia dato il seguente pattern:

(FTP|HTTP)://([_a-z\d\-]+(\.[_a-z\d\-]+)+)((/[ _a-z\d\-\\\.]+)+)* 

$1 = FTP|HTTP
$2 = [_a-z\d\-]+(\.[_a-z\d\-]+)+
$3 = \.[_a-z\d\-]+
$4 = (/[ _a-z\d\-\\\.]+)+
$5 = /[ _a-z\d\-\\\.]+

3. Esempi

Passiamo ora ad alcuni esempi: utilizziamo come fonte dati un file. Salva un qualsiasi file html da internet e rinominalo "Test.htm". Sia data una form: mettiamo sulla form 2 bottoni (btSearch e btReplace) e un Memo (Memo1).

3.1 Ricerca nel file "Test.htm" di tutti i link con descrizione dei valori di $1, $2, $3, etc...

procedure TForm1.btSearchClick(Sender: TObject); var    i, j: integer;   FileStream: TFileStream;   InputStr, InputFile: string;   RegExp1: RegExp;   MatchCollection1: MatchCollection;   Match1: Match;   SubMatches1: ISubMatches; begin   //   InputFile := 'Test.htm'; //file di input   FileStream := TFileStream.Create(InputFile, fmOpenRead);   SetLength(InputStr, FileStream.Size);   FileStream.Read(InputStr[1], FileStream.Size);   //carico in InputStril contenuto del file "Test.htm"   RegExp1 := CoRegExp.Create;   with RegExp1 do     begin      //cerco tutti i link. Questo pattern può essere modificato a nostro piacimento.       Pattern := '(FTP|HTTP)://([_a-z\d\-]+(\.[_a-z\d\-]+)+)' + '((/[ _a-z\d\-\\\.]+)+)*';       IgnoreCase := True; //ricerca "case insensitive"             Global := True; //ricerchiamo tutte le possibili corrispondenze col pattern       MatchCollection1 := Execute(InputStr) as MatchCollection;       //collezione dei match     end;   for i := 0 to MatchCollection1.Count - 1 do     begin       Match1 := MatchCollection1.Item[i] as Match;       Memo1.Lines.Add(Match1.Value);       SubMatches1 := Match1.SubMatches as SubMatches;       for j := 0 to SubMatches1.Count - 1 do         begin           Memo1.Lines.Add('          ' + '$' + inttostr(j + 1) +                           ' = ' + VarToStr(SubMatches1.Item[j]));         end;     end;   RegExp1 := nil;   FileStream.Free; end;

3.2 Ricerca nel file "Test.htm" di tutti i link e sostituzione dei match con una nuova stringa. Il risultato viene salvato nel nuovo file "Test_out.htm"

procedure TForm1.btReplaceClick(Sender: TObject); var   i: integer;   InFileStream, OutFileStream: TFileStream;   InputStr, OutputStr, InputFile, OutputFile: string;   RegExp1: RegExp;   MatchCollection1: MatchCollection;   Match1: Match;   SubMatches1: ISubMatches; begin   //   InputFile := 'Test.htm';   OutputFile := 'Test_out.htm';   InFileStream := TFileStream.Create(InputFile, fmOpenRead);   SetLength(InputStr, InFileStream.Size);   InFileStream.Read(InputStr[1], InFileStream.Size);   InFileStream.Free;   RegExp1 := CoRegExp.Create;   with RegExp1 do     begin       Pattern := '(FTP|HTTP)://([_a-z\d\-]+(\.[_a-z\d\-]+)+)' +                  '((/[ _a-z\d\-\\\.]+)+)*';       IgnoreCase := True;       Global := True;       OutputStr := Replace(InputStr, '$2');       //sostituzione dei match col valore di $2     end;    OutFileStream := TFileStream.Create(OutputFile, fmCreate);   SetLength(OutputStr, Length(OutputStr));   OutFileStream.Write(OutputStr[1], Length(OutputStr));   OutFileStream.Free;   RegExp1 := nil;   showmessage('replace terminato'); end;

4. Per finire

Links consigliati:

http://www.brettb.com/VBScriptRegularExpressions.asp
(da guardare anche i links in fondo alla pagina)
 

Cercare su Google: "VBScript Regular Expressions"

 
 
Your Ad Here