El misterio de los 200…

Aunque ya he comentado el contenido de esta entrada en el foro de facebook, necesitaba colgar de alg

Mi método doble valor y expresiones con LiveBindigns

Durante los próximos meses, vamos a ser muchos los compañeros que tomemos contacto con áreas nu

Feliz Navidad :-)

Estimados amigos y compañeros, os deseo una Feliz Navidad y un próspero año. Os deseo de corazón

 

Vacaciones…

agosto 8, 2011 en Entrada Diario

Hola,

Hoy colgamos en el blog el cartelito de “Cerrado por vacaciones”.  :-)

Por fin…  !!!!!!!!!!!!!

Volveremos a retomar la actividad tanto en el  blog, grupo de facebook y twitter ya en Septiembre (posiblemente la segunda semana). Me quedan por delante unas semanas de descanso y de desconectar de todo y aunque no me vaya a una isla desierta, es complicado estar pendiente de Internet.

Así que voy a coger el cubo, la pala y mi flotador y  me voy a la orilla de la mar, a construir castillos y a disfrutar del merecido descanso.  

;-)

¡Que seais muy felices y nos volvamos a encontrar en unas semanas!

Otras mandangas…

julio 24, 2011 en Código, Consejo, Delphi, Entrada Diario, Mis cosas, Taller práctico

 Dicen diversos diccionarios acreditados que una mandanga, en sentido familiar, es algo así como una excusa, como en “no me vengas con mandangas y ponte a trabajar…”. También puede ser un cuento, chisme o tontería, como “¡oye! ¡siempre sales con mandangas!. Incluso, alguna vez la escuché en un sentido figurado, de persona con ganas de pleito y barullo, como en “¡Mira que te gusta la mandanga!“… :-) Y en el caso de la entrada de mi blog, pienso que anda mas cerca de esa segunda acepción, propia de quien se aburre y sale con relicarios.  :-)

En esta ocasión, he rebuscado en el baul y he subido al servidor un pequeño ejemplo que, aunque no tenga valor en si mismo, por aquello de que fuera exquisito su código,  mezclaba un tanto de todo, y me daba algo de tiempo mientras reviso y buceo en otros temas. Los días van pasando demasiado deprisa y las vacaciones hacen que se huela el descanso y el ocio, lo cual va un poco reñido con el mantenimiento del blog. =:-O 

Además, siendo justos, dado que estamos aqui como quien dice entre amigos, diría verdad si reconociera que me fue util. Pero bueno… mejor que deciros que es o que no es, lo veais vosotros mismos.

Un segundo que prepare unas escenas. Upsssss
 

 ¿Veis…? Al ejecutar el fichero Impresion.exe (el cual queda residente y visible en la barra del sistema) me permitía ir procesando el contenido de unos ficheros de texto plano, de forma que, a medida que iban siendo creados, se leyera su contenido y se dispusiese en una lista de tareas.

Los ficheros siempre tenían la misma extensión. Para nuestro ejemplo los carácteres “sjc”.

En el video, se puede ver como copio al portapapeles uno de los ficheros (concretamente el primero de ellos lanzará la ventana del explorador con la dirección del blog) y lo pego en la carpeta donde reside el ejecutable. Y como resultado sucederá lo comentado. En la vida real, no pegaba ni cortaba ningun fichero sino que lo generaba desde mi aplicación principal, permitiendo delegar esa tarea a esa especie de “servicio”.

Estas eran las lineas que generaban el fichero, cuyo nombre era siempre aleatorio: 

procedure TDemoBlog.VerWebExecute(Sender: TObject);
var 
  Parametros, FFile: String;
begin 
 inherited;

  Parametros:= '11111#Probando un ventana explorer'+
               '#3#1#http://www.sjover.com/delphi#';
  FFile:= Utilidades.PalabraAleatoria(10) +'.sjc';
  Utilidades.SaveFile(FFile, parametros);
...

Y cada fichero generaba una linea de tarea que se mostraba en la ventana de la aplicación. En la imagen inferior podeis ver la ventana en la que existe un sencillo TListbox para mostrarlas.

 Las dos funciones PalabraAleatoria( ) y SaveFile(  ), pueden ser encontradas facilmente en internet con distintas variaciones.  

procedure SaveFile(const FileName: TFileName;
                   const content: string);
begin 
  with TFileStream.Create(FileName, fmCreate) do 
    try 
      Write(Pointer(content)^, Length(content));
    finally 
      Free;
    end;
end;
function PalabraAleatoria(Longitud: integer): string;
const
  Letras = '01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var
 n : integer;
begin
  Result:='';
  for n:=1 to Longitud do
    Result:=Result+Letras[1+Random(Length(Letras))];
end;

 Si tenéis curiosidad por ojear el codigo fuente lo podeis descargar desde el enlace siguiente: 

Descargar fuentes de la entrada

Dentro de las fuentes, leed por favor el archivo leeme.txt donde os comento unas anotaciones necesarias.

Básicamente, existen dos clases: la clase que representa a la tarea a procesar (TTrabajo) y la clase que representa a la cola de tareas (TColaImpresion), con características similares a cualquier abstacción de tipo Cola. Mas abajo podeis ver un extracto del interfaz. La clase TColaImpresion, se apoya en una instancia de la clase TStrings, o lista de cadenas, que suele ser un recurso bastante comun y recurrido, para almacenar las tareas hasta que sean ejecutadas. Y finalmente, dos piezas importantes en el esquema son por un lado el componente Notificador (TShellNotification), que nos ayuda a descubrir que ha sido generado un nuevo fichero, y por otro una instancia descendiente de TTherad que va consumiendo, en un hilo secundario, las tareas de la cola, siguiendo el orden de antiguedad. 

  TColaImpresion = class(TComponent)
      ...
  protected 
    procedure DoAddTaskEvent(ATrabajo: TTrabajo); virtual;
    procedure DoDeleteTaskEvent(ATrabajo: TTrabajo); virtual;
    procedure DoHacerTaskEvent(Sender: TObject; ATrabajo: TTrabajo;
    AModulo, AFuncion: Integer); virtual;
    function DoLinkFileCreateEvent(Sender: TObject;
     Path: String): TShNotificationEvent; virtual;
    procedure Delete(Index: Integer);
  public     { Public declarations } 
    constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;
    procedure Add(const ATextoAMostrar: String; ATrabajo: TTrabajo);
    function Count: Integer;
    function ExtraeItem: TTrabajo;
    procedure VaciarCola;
    procedure UpdateCola;
    procedure DoHacer;
    procedure LeeFicheroImpresion(const AFileName: String);
    property Strings[Index: Integer]: string read Get; default;
    property Path: String read FPath write SetPath;
    property Active: Boolean read FActive write SetActive;
    property OnAddTaskEvent: TNotifyTaskEvent read FOnAddTaskEvent write SetOnAddTaskEvent;
    property OnDeleteTaskEvent: TNotifyTaskEvent read FOnDeleteTaskEvent write SetOnDeleteTaskEvent;
    property OnDoHacerEvent: TNotifyDoHacerEvent read FOnDoHacerEvent write SetOnDoHacerEvent;
  end;

La pregunta que me queda responder es quizás el por qué.   :-)

No… no es que estuviera aburrido… :-)    Habitualmente uno escribe código porque existen nuevas necesidades o requerimientos que satisfacer, en un marco de trabajo mutable y caprichoso. Es lo mas normal. Sin embargo, en ocasiones, esas circunstancias pueden ser simplemente, que estemos encarando la actualización del entorno de desarrollo, y exista -a nuestro pesar- la necesidad de replantearnos el uso en el proyecto de un determinado paquete de componentes por otro que pueda ofrecer mejoras -o sean libres, o sea requerimiento del cliente, etc…-. Así, de la noche a la mañana y sin quererlo ni beberlo, nos veamos obligados a sustituir todo el codigo que afecta a los antiguos componentes por el codigo nuevo y con la necesidad de que el sistema siga funcionando, el nuevo entorno permita compilar sin los paquetes sustituidos y ademas, uno tenga el tiempo de hacer eso sin tener que abandonar el mundo y encerrarse en una cueva… Y eso es otra historia. Y además, por experiencia, amarga…  :-)    

Realmente, estas líneas de codigo las escribí en el contexto de plantearme el cambio del sistema de impresión, de la suit de componentes que utilizaba. El proyecto contenía tropecientos mil informes distintos a cada cual mas intrincado y los generadores de informes, pese a ser muy similares unos de otros, son el peor sitio para perderse cuando uno tiene prisa. 

Así que estas lineas fueron simplemente una estrategia para dilatar ese problema en el tiempo, de forma que fuera llevadero y asumible. Las unidades que iba a extraer del proyecto fueron aisladas dentro del modulo de impresión, de forma que el proyecto compilaba sin las referencias a los paquetes que dejaba, ya en el marco del entorno actualizado. Seguia teniendo el problema de la migración, porque eso no me lo resolvía, pero sí podía compilar e iniciar la sustitución progresiva de las misma. Por otro lado, tampoco era tan mala idea. He visto otras aplicaciones trabajar así. El servidor de fax de la empresa tiene un sistema similar: Habilita una carpeta compartida al alcance de todos los clientes de fax del sistema. y para enviar un fax, son creados simultaneamente un documento de texto que parametriza el envío y otro documento que representan los datos a enviar. Y poco mas. El servidor de fax se limita a monitorizar la carpeta y va procesando los ficheros anotando el exito o fallo de la operación de envío.

En este caso simplemente habia reservado una carpeta local para depositar los ficheros que representaban cada informe. Seguro que se os ocurren algunos detalles más (que un fichero de texto plano se puede compartir, que la aplicación se comparte con cualquier otra aplicación que sea capaz de escribir en un fichero de texto, y que además, se centraliza el uso de los recursos de impresión, siendo susceptible de monitorizar el consumo del mismo, etc, etc…).

Nada mas por hoy. Espero que estas lineas os puedan ser de alguna utilidad.  

 

 

La web de JM

julio 4, 2011 en Código, Componentes, Delphi, Enlace interesante, Entrada Diario, Nos deja la semana...

Quisiera compartir con vosotros lo que me parece una buena noticia para nuestra comunidad y se que algunos compañeros se van a alegrar. Y es que, tras cruzar unos correos con mi amigo Jose Manuel Navarro, me ha permitido reactivar su blog dentro de mi servidor, de forma que pueda ser accesible de nuevo.

:-)   

Así que aprovecho estas lineas para darle las gracias a Jose Manuel. Ya he comentado anteriormente, en las paginas de mi blog, que es un gran amigo, que conozco desde hace ya bastantes años.  

Hay muchos compañeros que seguían al dia de hoy solicitandole código existente en su blog (yo he recibido recientemente uno de los correos que fue el que realmente me alertó de esa posibilidad de alojar sus paginas), y a mi particularmente, me parecía que, dada la calidad de sus artículos, merecia ser rescatado del silencio, aunque realmente no se haga ésto para darle continuidad.  También quisiera destacar que antes de mi, fue Jose Luis en el rinconcito, quien alojó una gran parte de los mismos. Yo he preferido mantener íntegra la apariencia de su blog, de forma que resulta hasta curioso porque para muchos va a resultar como ver una fotografia congelada en el tiempo, alla por el año 2006.

Espero que os gusta la iniciativa y que me vayais comentando -o directamente a Jose- si existe algun problema con alguno de los enlaces.

La web de JM 

Para el código, Jose Manuel, tambien ha habilitado un repositorio en el que se han añadido tanto código como artículos en https://github.com/jmnavarro, y que puede servir en un futuro para alojar mas contenido propio, sea o no de nuestra herramienta o comunidad. Seguro que, de cualquier forma, será tan interesante su lectura como lo fue la de los articulos que escribió para Delphi y C++ Builder.

No quisiera despedir la entrada sin comentar que lamentablemente, toda la parte que hace referencia a sus escritos vinculados con Bolivia, que reflejan su humanidad y su solidaridad, finalmente no pude incluirlos, dada su extensión y la cantidad de referencias que habían de rehabilitarse, en una estructura web clásica como lo era su blog. Lo digo porque muchos de los que visitaron su pagina posiblemente se pregunten donde están y realmente me apenó verme en la tesitura de excluirlos.

Nada mas, me despido deseando que los disfruteis de nuevo.

Cerrando temas…

junio 23, 2011 en Delphi, Entrada Diario, Nos deja la semana..., Noticias, Noticias Delphi

 

Estas semanas,- tanto en lo personal como en lo profesional-, han estado marcadas por una idea mas o menos constante: la idea de cerrar temas y pasar página.

Posiblemente, vosotros tambien hayais  sentido esa necesidad, si no ahora, en otro momento…, aunque, siendo justos, por estar tan cercanas las vacaciones de verano, se presta a ello especialmente; de forma que la idea de descansar de verdad, de verdad de la buena, con la conciencia de que no se quedan temas pendientes, es altamente tentadora.

En lo personal, gracias a Dios, la semana ha traido el fin de las mini-reformas en casa, que me han tenido liado, fin de semana tras fin de semana, con las chapuzas propias de los tiempos de crisis, donde uno aprende a elegir la pintura para pintar una habitación, a distinguir una brocha de un rodillo y a descubrir algo importante: que nunca se debe poner por debajo de la altura de la herramienta, sobretodo porque suele acabar manchado.

Las crisis tienen este tipo de cosas, y hacen que surjan los chapuceros como setas (¡ojo que lo digo por mi!), que igual te sirven para un roto que para un descosido: Así que pinté la habitación que estaba arreglando, mi antiguo espacio de trabajo (cuasi Santuario), para que fuera invadido por la reina de la casa, mi hija… ¡Para que luego digan que los programadores no servimos para nada…!  Estas manitas tambien han pintado, desmontado las enstanterias, movido cajas y cajas de libros y trastos y finalmente, pulido el suelo de la habitación…  :-)    Sin contar las toneladas de papeles inútiles que he ido rompiendo a lo largo de las semanas y que han llenado mas bolsas de basura de las que hubiera imaginado…  :-D

¡Oye!… ¡entre nosotros!, ¡que se ha quedado de cine!. Ayer trajeron los muebles, y el esfuerzo y las semanas de penurias habían valido la pena.

También durante esta semana acabaron las clases de repaso de Inglés que he recibido durante bastantes meses. Puestos a dar la lata a nivel internacional e incomodar a toda la Comunidad Anglosajona, era necesario hacerlo con un poco más de clase.  :-)    jajaja 

No, no es que fueran muchas horas semanales, porque han sido solo un par de horas repartidas en dos días semanales, pero sí las suficientes para que tuviera que sacrificar mis entrenamientos, tras la jornada laboral.

Y además, fin del curso de los mayores, hasta Septiembre que lo retomemos, con gente nueva y no tan nueva. Las aulas quedarán vacías otra vez, y nuevamente, en una rutina que se ha repetido año tras año, volverán a pegarse de tortas para venir a su clase de informática.  :-)   Unos volveran y desgraciadamente alguno fallará a la cita, ejemm… ejem…  algo que es ley de Vida. :-)   

Me han enseñado mucho (más ellos a mi que yo a ellos  :-) ). Porque en esas clases uno se da cuenta cómo los sistemas que creamos,  nuestro hardware, nuestro soberbío software del que nos vanagloriamos, ignora a menudo a una población cada vez mayor, la de nuestros padres y abuelos.

Interfaces poco adecuados a su vista y condiciones, y sistemas complejos para personas que no tuvieron la oportunidad de crecer con la tecnología.  Botones diminutos. Alertas visuales ridiculamente escondidas. Ventanas con trampa que esperan ser clickeadas sin que el usuario ni siquiera lo sepa. Nadie piensa en ellos. Cualquiera que trabaje con nuestras herramientas sabe que los destinatarios de nuestras aplicaciones no son esta población. Un día se me ocurrió ponerles el interfaz de alto contraste y me miraron ofendidos: ¡chico…! ¡te pensaste que soy rompetechos y que ya no me veo…!. Casí me pegan. ¿Tanta tecnología y a nadie se le ocurre crear un perfil de usuario predeterminado para personas mayores? Algo creado especificamente para ellos, con programas adaptados especialmente para sus problemas. Es una frivolidad pensar que cambiando la resolución de la pantalla se arregla el problema.

Sin embargo, creo que todo tiene su recompensa, sobretodo cuando ves su carita de asombro y felicidad, si son capaces de escribir una correo a sus hijos, de compartir la foto de su nieto a traves de una red social o simplemente de salir de su espacio vital y conocer otros navegantes, en esa inmensa soledad que se convierten a menudo sus habitaciones, ya en el hogar familiar o en la residencia, donde con frecuencia acaban por ser abandonados. Ese cariño que te transmiten en momentos determinados también te ayuda a ser mejor programador y a pensar que no somos el ombligo del mundo.

Y ya que hablamos del centro de mayores, comentaros también que estas semanas se retomó de nuevo la puesta en marcha del programa que compartimos en el blog en las entradas “Un día con los mayores”. El programa, se lo regalé al Centro (a coste cero patatero),  en estas fechas en el año anterior, y quedó pendiente tan solo de la toma de datos, rellenar las fichas, etc, etc. que era un labor ya del personal. Pero se pasó el plazo de las inscripciones a las actividades y el proceso se interrumpió y se pensó en la conveniencia de esperar estos meses pasados. Cuando hace un mes retomé el tema para ver como lo habian llevado, descubrimos que la persona que había grabado los datos, un chavalito joven contratado para ese menester, jajajaja escribía igual que enviaba mensajes de movil. :-D   por lo que, puestos a escoger entre revisar los datos (desgraciadamente llenos de faltas de ortografia e irregularidades) y pasarlos correctamente se optó por hacer borron y cuenta nueva. Esté mes, les preparé algunas modificaciones para que fuera mas ágil la toma de datos y el proceso de inscripción. Falta simplemente añadir un lector de codigos de barras y creando un carnet con los datos básicos para evitar un porcentaje de búsquedas, van mas que apañados.  :-)

Asi que otro tema cerrado… Poco a poco voy dejando zanjado temas a diestro y siniestro, quedando tan solo pendiente de mi actividad principal en la empresa.

Y en el blog sigue la tranquilidad. Nuestra comunidad inició otra fase de calma chicha, donde hay pocas novedades, lejos de los meses que preceden al lanzamiento de cada versión. Aun así existen apuntes interesantes que merecen ser destacados:

 * Un nuevo programa de certificaciones para desarrolladores Delphi: http://www.embarcadero.com/certification, que va a permitir evaluar los conocimientos en dos niveles o programas distintos (Delphi Developer Certification y Delphi Master Developer Certification). En España, Danysoft facilitará el acceso y desarrollo de los mismos, impulsando algunos cursos que ayuden a obtener la certificación. Sin entrar en cuestiones sobre si esto en si es necesario o no lo es, cada cual puede tener su opinión, lo cierto es que siempre ha podido existir esa demanda por parte de la comunidad de programadores, aunque haya ido emergiendo y ocultandose como los ojos del Guadiana. 

Podeis acceder a esta información en:

Programa de Certificación Delphi

 

 

*  Para los que estabamos esperando nuevas publicaciones, recientemente hemos compartido en facebook la aparición del nuevo libro de Marco Cantú “Delphi XE Handbook“,

 que puede ser adquirido bien a través de Amazon o en formato digital en el sitio FastSpring

El libro, podría considerarse una extensión de su última publicación y se complementa con éste. De hecho, el autor ha considerado las cuatro ultimas publicaciones dentro de una oferta conjunta que podeis visitar en:

http://www.marcocantu.com/handbooks/

Tardará un poco en ser traducida pero me imagino que será algo que ya esté en mente de Embarcadero, para que pueda llegar finalmente a los desarrolladores hispanos en español, al igual que se hizo con su publicación sobre las novedades en Delphi 2010.

* Con fecha 16 de Junio, resalté en Facebook el articulo de Jon L. Aasenden, Beginning of the end for the .net framework?. En cierta forma creo que la entrada de su blog refleja una inquietud que se hace palpable y manifiesta, y que no tiene que ver tanto sobre la caducidad de punto net sino la incertidumbre de no saber bien hacia donde vamos. Si bien nadie pone ya en duda que Delphi no solo no ha muerto sino que anda en los tablaos flamencos de juerga en juerga, disfrutando de la vida que le da la supervivencia de las aplicaciones de escritorio y el que la nube no deje de ser una quimera y el framework de punto net un niño gordo y pesado, harto de chocolatinas y dulces. Casualmente esta semana hojeaba en mi trabajo un ejemplar de Computerword, donde aparecian retratados y engominados los directivos de las empresas mas pesadas y relumbrantes del pais, y detras de la parafernalia y de los tecnicismos, la gomina y las grandes palabras, era demoledor leer un sencillo articulo donde se reconocia que nadie se atreve a poner las aplicaciones críticas donde no las pueda tener a buen recaudo. Entonces uno se pregunta: ¿para que tanto bombo y platillo?

Delphi sigue ganando algunos premios en el 2011,

http://www.codeproject.com/PressReleases/3935/The-Code-Project-Announces-Third-Annual-Members-Ch.aspx,

y sigue siendo considerado por una mayoria de programadores como una de las mejores herramientas de desarrollo para aplicaciones nativas Windows. De hecho, creo que la comunidad recibió como algo bueno la compra de Microsoft de Skype por 8.5 b. de $, que tradicionalmente se había considerado una aplicación hecha con Delphi (aunque luego realmente no sea toda ella como muchos apuntaban).

Ya… si… (de momento para 32 bits)… pero tened paciencia. En nada vamos a estar desarrollando con 64 bits (a la vuelta de la esquina), para Mac y para Linux. ¡Y si no, tiempo al tiempo!  

* Y finalmente nos queda comentar que está próximo el fin de la última promoción de Embarcadero, a la que podeis acceder en España desde el enlace:

Aprovecha el momento Rad Studio XE

Si no me equivoco, la oferta está vigente hasta el 30 de Junio.

http://www.embarcadero.com/radoffer

Como sabeis, la parte principal de la oferta es la posibilidad de obtener Rad Studio al precio de Delphi, que es lo que a priori la puede hacer interesante.

Respecto al blog, estoy valorando algunos cambios, con la única motivación de que os pueda ser realmente util y atractivo. Estoy en ello y ya he comentado algo de ésto en el Grupo de Delphi Básico en Facebook. Aquí creo que es la primera vez que hago referencia. La idea al final, es que sea mucho mas dinámico y que preste un servicio real a la Comunidad. Esa fue siempre la intención aunque a lo largo de estos años, no siempre se pudiera conseguir. 

De hecho, incluso me planteo cambiar el título del blog y que deje de ser Delphi Básico. Este nombre seguía un ideario en el que me parecía importante (desde siempre): la necesidad de seguir hablando de lo básico y de lo sencillo. Cuando se acordaban desde la redacción de Sintesis en Albor, qué temas ibamos a abordar en los siguientes articulos, creo que siempre hacia hincapié en esa necesidad, más teniendo en cuenta el contexto de aquellos días en donde existía menor cantidad de recursos y enarbolabamos la lanza de la necesidad de tener el entorno en nuestro idioma. Estaba muy sensibilizado con ese punto, y desde siempre, ha sido un tema sobre el que giró el trasfondo de muchas de las entradas que se pueden leer.

Hoy creo que hay otras palabras que encajan mas en mi forma de entender el contenido del blog: la comunidad, el compromiso y  la solidaridad con quienes realmente no disponen de medios, la huída de las palabrerias, con las que los liantes acostumbran a salvar las apariencias, y en definitiva, servir de puente entre personas, culturas y comunidades. Es por eso que valoro que Delphi básico se convierta en Delphi solidario.  :-)

Y parte del trabajo real del blog y de ese esfuerzo, sucede entre bastidores y realmente, las entradas que podeis leer son asi como los icebergs. Es la parte mas visible pero queda una menos notoria pero mayor, dado que buena parte de mi tiempo lo dedico a buscar recursos en el día a día para el blog. Algo que llevarme a la boca… jajajaja. :-)  Cada vez que encuentro algo que puede ser interesante en cualquiera de las redes sociales: un enlace, una entrada de un blog, sea de la comunidad que sea, no hay tiempo que perder para que lo podais tener en el grupo de facebook o reenviadas en twitter. Ese sí me parece un buen pegamento que una a las Comunidades, lejos de una concepción nacional y restringida.

Seguiremos comentando las novedades que puedan surgir en el blog y en las Comunidad en las siguientes entradas.

Que paseis un buen fin de semana.

 

 

 

 



Expresiones regulares: Novedad en XE (y Parte 3.2)

mayo 31, 2011 en Delphi, Enlace interesante, Entrada Diario, Taller práctico, XE

Con esta última entrada que vamos a compartir, creo que podemos dar por cerrada la serie sobre Expresiones Regulares, al menos en lo que respecta estrictamente al blog. Sin embargo, si que me gustaría anticiparos y compartir con vosotros, de la misma forma que lo he hecho con el grupo que se abrió en Facebook, que voy a intentar mantener vivos y dinámicos algunos de los contenidos relacionados con las entradas, de forma que puedan ser consultados o comentados cuando ya no sean de inmediato interés o las entradas ya no habiliten la posibilidad de comentar porque ha caducado el plazo. Es por eso que existe una nueva opción en el menú “Temas de discusión”, inaccesible de momento, pero que se activará si Dios quiere en un breve espacio de tiempo, (que ahora mismo no os puedo confirmar): ¿una semana? ¿un mes? Ni idea. Posiblemente, sea necesario ser miembro del grupo de Delphi Básico en Facebook para acceder al mismo.

De momento he creado cuatro temas de discusión que me parecen interesantes y que están directamente relacionados con entradas de estos últimos meses o del año anterior: el framework del conjunto de entradas de Un día con los mayores, el módulo de control de presencia, el componente TPanelMiniaturas que pudimos compartir con Germán y por último, el que representa a esta última serie sobre Expresiones Regulares.

Todo esto esta un poco en lo alto, madurando y casi diría fermentando… :-)

Cualquier comenario que deseeis hacer sobre esto, lo podeis escribir en el muro del grupo de facebook (no en mi cuenta de facebook personal por favor).

Y por fin llegaron los postres…

Delphi 2007: PerlRegEx y su componente TPerlRegEx

Si hemos optado por Delphi 2007 o cualquiera de las versiones anteriores incluidas en los componentes que descargamos. ¡Enhorabuena!. Has sido un tipo listo, porque no tienes por que esperar a la renovación del IDE para disfrutar de ”algo” que te pueda ahorrar un esfuerzo y un trabajo, con independencia de que este sea más o menos grande. No… no es que ahora sea todo reducido a una expresión regular. No… la idea es mas sencilla, más simple: Si encuentras un motivo para usarla, hazlo, porque posiblemente sea una buena inversión, ya que su uso va a ahorrarte bastantes lineas de código y además no va a ser algo que va a crearte problemas en esa posible actualización del entorno, si decides actualizarte a XE.

Vamos a entender este punto:

Una vez instalado el componente en el IDE podremos usarlo de la forma habitual, como cualquier otro componente de la paleta. En mi caso, y en el primero de los ejemplos, hemos hecho uso en tiempo de diseño de forma que nos despreocupamos tanto de la creación como de la destrucción. Tan solo debemos centrarnos en 3 aspectos claves:

  • Necesitamos asignar la expresión que se va a usar.   
    • (pre.RegEx:= edCadenaBusqueda.Text;)
  • Necesitamos asignar el texto que se va a evaluar de acuerdo a esa expresión
    • (pre.Subject:= s;)
  • Y finalmente, necesitamos invocar el procedimiento que pone en marcha el proceso de analisis y compilación de la expresión
    • (if pre.Match then...) 

 A partir de ese momento, ya nos es posible evaluar si ha tenido exito gracias al método FoundMatch. También podemos conocer qué posición ocupa en primer caracter encontrado, que cumple la expresión, gracias a MatchedExpressionOffset y que longitud tiene el término MatchedExpressionLength.

 Y en el caso de querer seguir buscando la expresión, simplemente sería algo tan sencillo como usar MatchAgain, nos avanzaría hasta la siguiente posición encontrada, de la misma forma que hemos comentado en el parrafo anterior.

En el ejemplo se puede ver claramente como avanzamos de forma progresiva a lo largo del texto, hasta encontrar todas las palabras que cumplen la expresión regular.

 Término   Posición   Longitud 
Mancha 19 6
hidalgo 95 7
rocín 149 5
rocín 702 5
hidalgo 760 7

 Guardar en la mente que estamos trabajando con cadenas no Unicode sino AnsiString. Esto lo vamos a ver de reojo en el apartado próximo. Pero antes, y volviendo al comentario que iniciaba éste, la mecánica de trabajo que vamos a utilizar por ejemplo desde Delphi XE, a nivel de la unidad RegularExpressionsCore va a ser similar. Cambian los collares pero los perros son los mismos.

Podemos comparar esos chuchos:   :-)

Salvando que ahora necesitamos crear y destruir dinámicamente la instancia de TPerlRegEx, puesto que ya no tenemos un componente sino un objeto, los metodos que usamos tienen nombres similares como era de esperar. A fin de cuenta estamos usando una importación de la librería y ya se han abstraido los conceptos aplicados a la funcionalidad. Asi pues tenemos de igual forma las propiedades RegEx y Subject, que esperan respectivamente la expresión y el texto a evaluar. Seguimos teniendo Match y MatchAgain. Y tan solo respecto a nuestro ejemplo, nos ha variado que MatchedExpressionOffset ahora es MatchedOffset y MatchedExpressionLength se convierte en MatchedLength.

No parecen unos cambios muy complejos…  :-)

 

RegularExpressionsCore Vs RegularExpressions 

 

Si nos decidimos a ejecutar el ejemplo 2, que utiliza el módulo RegularExpressionsCore, obtenemos un resultado un tanto diferente del ejemplo anterior. Permitidme unos cambios rápidos en el código… veamos… cambiad tanto en el cuerpo de la accion Iniciar como en Seguir las dos lineas, de forma que sean similares a las que teníamos en el primer ejemplo:

      rchContenedorTexto.SelStart:= ConvertirOffSet(pre.MatchedOffset-1);
      rchContenedorTexto.SelLength:= Length(UTF8ToString(pre.MatchedText)); 

 por

      rchContenedorTexto.SelStart:= pre.MatchedOffset-1;
      rchContenedorTexto.SelLength:= pre.MatchedLength; 

Esta es la nueva lectura que podemos hacer: 

 Término   Posición   Longitud 
Mancha 19 6
hidalgo 96 7
rocín 150 6
rocín 717 6
hidalgo 776 7

 ¡Veis! Comparad unas y otras. Ya no nos coinciden y por lo tanto, las selecciones de texto en nuestro componente RichEdit andan digámoslo como desenfocadas. 

:-D

Esa es la magia de Unicode frente a AnsiString. Las vocales acentuadas que aparecen en algunas palabras del texto (como vivía, rócín, más, etc…) van a retornar un Offset y un valor de Length aparentemente incorrectos y ciertamente, esto no es exactamente así. Por tal motivo he utilizado en el ejemplo 2 un array de enteros y una función que corrige la posición.

El motivo de esta contrariedad es simple y de forma llana porque estamos trabajando con cadenas unicode UTF8String, y la codificación de éstas varía entre 1 y 4 bytes según el punto de codigo que consideremos. Para todos aquellos caracteres con mas de 1 byte, ese byte posterior al primero, tanto si consideramos el de dos, tres o cuatro bytes, tiene siempre la estructura binaria [10xxxxxx] donde esos primeros 2 bits (10) son fijos y el resto variable, dependiendo en conjunto de la codificacion del resto de bytes. Eso nos ha permitido de forma sencilla recontar los caracteres (descartando en el conteo los caracteres que representaban ese byte posterior)  y recalcular el offset (ConvertirOffSet( ))  

Hay un ejemplo muy bueno en Wikipedia que explica visualmente este punto. Os aconsejo la lectura así como la del libro de Marco Cantú “La guia de Delphi” (2010) donde se habla abiertamente del cambio a Unicode en nuestro entorno de desarrollo.

 http://es.wikipedia.org/wiki/UTF8

http://es.wikipedia.org/wiki/Archivo:Codificaci%C3%B3n_UTF-8.svg

 

Todo esto comentado, va a influir en nuestra decisión de elegir entre las dos formas de trabajo distintas: una por decirlo de alguna forma de más bajo nivel, RegularExpresionsCore, que manipulará tanto a nivel de parámetros como retorno de funciones cadenas UTF8String. De hecho, en el constructor de la clase, se ocultan las opciones de creación que por defecto, para que esto no pueda ser cambiado, por razones obvias. Notad como estas constantes no estan definidas en el conjunto de opciones TPerlRegExOptions, que tambien van a combinarse previa a la compilación.

constructor TPerlRegEx.Create;
begin
  inherited Create;
  FState := [preNotEmpty];
  FCharTable := pcre_maketables;
  FPCREOptions := PCRE_UTF8 or PCRE_NEWLINE_ANY;
end;

Y por otro lado RegularExpressions, una capa por encima de la anterior, permitiendo que trabajemos directamente con nuestras cadenas habituales String unicode, de forma que para nuestra selección del texto, no nos ha hecho falta ningun mecanismo adicional que haga el recalculo. Por lo demas, podeis usar sin problema tanto una forma como otra. Si os hace falta obtener el mapa de coincidencias, lo mas sencillo es que os decidais por la capa de nivel superior.

 Bueno. Nos despedimos aquí. Es posible que queden algunas dudas. Mi consejo en ese caso es que activeis la depuración de los dcus en las opciones del compilador y recorrais con puntos de parada también las clases que participan. Es una buena forma de comprender que sucede realmente entre bastidores.

 Espero que os haya gustado y que os pueda ser útil.

 

Entradas relacionadas: 

Join the forum discussion on this post

Expresiones regulares: Novedad en XE (Parte 3.1)

mayo 28, 2011 en Delphi, Entrada Diario, Taller práctico, XE

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.

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:

Interfaz con los cambios

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…