Seguimos avanzando en la serie…
Como os comentaba en la parte segunda, existía un pequeño problema, que mientras se corrige y no se corrige, nosotros podemos salvar de formas distintas. Vamos a buscar la mas sencilla para este caso concreto.
- TRegEx frees FRegEx while TMatch et al still have a reference to it
- Bug in Delphi XE RegularExpressions Unit
Veamos..
En nuestro caso, vamos a eliminar el boton [Seguir] dejando en una sola acción el recorrido de la búsqueda, de forma que la instancia no se sale de ambito hasta que llega al final del método, momento en el que son liberados los recursos asociados a la instancia creada de TRegEx. Eso lo podéis comprobar de una forma muy sencilla poniendo un punto de parada y comprobando que efectivamente es destruida la instancia de TPerlRegEx que es la clase que en ultima instancia liberará la memoria del proceso.
Este tipo de errores son bastante dificiles de detectar en un ambiente de trabajo normal (me refiero a fuera de lo que puede ser un ejemplo de pocas lineas) pues no siempre tienen que generar excepciones inmediatas y suelen ofuscarse entre las lineas del entorno inmediato que la rodean.
Así pues… eliminamos la acción asociada a Seguir y el componente TBitBtn que la disparaba. Tampoco nos hará falta la variable FSeguir ni los update de las acciones. Y seguidamente hacemos unos pequeños cambios en el cuerpo del procedimiento vinculado a la accion acIniciar. Nos basta alojar un el bucle que recorra las coincidencias y dejarle al usuario la opción de seguir o no. Esto último nos ha hecho valorar que lo que era un procedimiento, que habíamos llamado EvaluarResultado( ) ahora sea una función y devuelva el deseo de seguir adelante o no de nuestro usuario.
Estas son las lineas que hemos modificado:
procedure TBusquedasConExpRegulares.acIniciarExecute(Sender: TObject); var pre: TRegEx; begin //vaciamos la lista de resultados lbxResultados.Items.Clear; //inciamos la creación personalizada de la expresión regular pre:= CrearExpresionRegular; // obteniendo la colección de resultados tras ejecutar la expresión CollOfMatch:= pre.Matches(rchContenedorTexto.Text); //nos valemos de un enumerador para recorrer la estructura EnumCollOfMatch:= CollOfMatch.GetEnumerator; //evaluamos el resultado de la busqueda while EnumCollOfMatch.MoveNext and EvaluarResultado do; end;
function TBusquedasConExpRegulares.EvaluarResultado: Boolean; begin with EnumCollOfMatch.Current do begin //añadimos la subcadena encontrada lbxResultados.Items.Add('La palabra ' + Value + ' ' + 'ha sido encontrado en la posición ' + IntToStr(Index-1) ); rchContenedorTexto.SelStart:= Index-1; //Su posición en el texto inicial rchContenedorTexto.SelLength:= Length; //Su longitud TextoBarraEstado(Length, Index-1); //Y lo mostramos además en la barra end; //de estado Result:= MessageDlg('Coincidencia encontrada...', mtWarning, [mbYes, mbNo], 0, mbYes) = mrYes; end;
Entonces quedaría de la siguiente forma ya con los cambios que hemos hecho:
unit UBusquedasConExpRegulares;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, CheckLst, ComCtrls, ActnList, RegularExpressions;
type TBusquedasConExpRegulares = class(TForm) edCadenaBusqueda: TEdit; bnIniciar: TButton; Label2: TLabel; Label1: TLabel; chlxOpciones: TCheckListBox; lbxResultados: TListBox; rchContenedorTexto: TRichEdit; Label3: TLabel; Label4: TLabel; Acciones: TActionList; acIniciar: TAction; sbBusqueda: TStatusBar; Button2: TButton; acReemplazar: TAction; Label5: TLabel; edCadenaAReemplazar: TEdit; procedure FormCreate(Sender: TObject); procedure acIniciarExecute(Sender: TObject); procedure acReemplazarExecute(Sender: TObject); private { Private declarations } procedure TextoBarraEstado(ALength, AOffset: Integer); function EvaluarResultado: Boolean; function CrearExpresionRegular: TRegEx; public { Public declarations } end;
var BusquedasConExpRegulares: TBusquedasConExpRegulares;
CollOfMatch: TMatchCollection; EnumCollOfMatch: TMatchCollectionEnumerator;
implementation
{$R *.dfm}
procedure TBusquedasConExpRegulares.acIniciarExecute(Sender: TObject); var pre: TRegEx; begin //vaciamos la lista de resultados lbxResultados.Items.Clear; //inciamos la creación personalizada de la expresión regular pre:= CrearExpresionRegular; // obteniendo la colección de resultados tras ejecutar la expresión CollOfMatch:= pre.Matches(rchContenedorTexto.Text); //nos valemos de un enumerador para recorrer la estructura EnumCollOfMatch:= CollOfMatch.GetEnumerator; //evaluamos el resultado de la busqueda while EnumCollOfMatch.MoveNext and EvaluarResultado do; end;
procedure TBusquedasConExpRegulares.acReemplazarExecute(Sender: TObject); var pre: TRegEx; begin //vaciamos la lista de resultados lbxResultados.Items.Clear; //inciamos la creación personalizada de la expresión regular pre:= CrearExpresionRegular; //reemplazamos todas las coincidencias rchContenedorTexto.Text:= pre.Replace(rchContenedorTexto.Text, edCadenaAReemplazar.Text); end;
function TBusquedasConExpRegulares.CrearExpresionRegular: TRegEx; var i: Integer; fOp: TRegExOptions; begin //vaciamos el conjunto de opciones para aplicar fOp:= []; // y procedemos a obtener las validas for i := 0 to chlxOpciones.Count - 1 do begin if chlxOpciones.Checked[i] then case i of 0: fOp:= fOp + [roIgnoreCase]; 1: fOp:= fOp + [roMultiLine]; 2: fOp:= fOp + [roSingleLine]; 3: fOp:= fOp + [roIgnorePatternSpace]; 4,5:; //4: fOp:= fOp + [preAnchored]; //5: fOp:= fOp + [preUnGreedy]; 6: fOp:= fOp + [roExplicitCapture]; end; end; // Creamos el registro para manejar la expresión regular Result:= TRegEx.Create(edCadenaBusqueda.Text, fOp); end;
function TBusquedasConExpRegulares.EvaluarResultado: Boolean; begin with EnumCollOfMatch.Current do begin //añadimos la subcadena encontrada lbxResultados.Items.Add('La palabra ' + Value + ' ' + 'ha sido encontrado en la posición ' + IntToStr(Index-1) ); rchContenedorTexto.SelStart:= Index-1; //Su posición en el texto inicial rchContenedorTexto.SelLength:= Length; //Su longitud TextoBarraEstado(Length, Index-1); //Y lo mostramos en la barra end; //de estado Result:= MessageDlg('Coincidencia encontrada...¿Seguir?', mtWarning, [mbYes, mbNo], 0, mbYes) = mrYes; end;
procedure TBusquedasConExpRegulares.FormCreate(Sender: TObject); begin rchContenedorTexto.Lines.LoadFromFile('texto.txt', TEncoding.UTF8); end;
procedure TBusquedasConExpRegulares.TextoBarraEstado(ALength, AOffSet: Integer); begin with sbBusqueda do begin sbBusqueda.Panels[0].Text:= 'Length:'+ IntToStr(ALength); sbBusqueda.Panels[1].Text:= 'OffSet:'+ IntToStr(AOffSet); end; end;
end.
Seguimos avanzando…
Muy buena esta serie. Como siempre, cuentas con mi admiración y mi agradecimiento por tus comentarios.
Un saludo
Me gustaMe gusta
Gracias Javier.
Gracias Germán.
Todos los comentarios se agradecen y por supuesto alientan y estimulan a seguir trabajando por nuestra Comunidad. Es más… Creo que lo peor que nos puede pasar es que todo este trabajo y horas dedicadas no generen ilusión y sea algo que nos deje indiferentes.
🙂
Subí ayer al servidor el último fragmento de la serie, que estaba por acabar. Por eso el retraso en este comentario.
Un saludo,
Salvador
Me gustaMe gusta
Gran artículo Salvador.
Fantástico material. Tanto la explicación como los ejemplos. Para mi es un tema complejo y con lo publicado queda mucho má claro y parece muuucho más sencillo. 😉
Enhorabuena.
Me gustaMe gusta