Boletín Pascal #2
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
![]() |
Boletín Pascal #2 INDICE 1. UNAS PALABRAS DEL EDITOR 2. BUSCADOR DE ARCHIVOS Alternativa para no subclasar y reemplazar un componente Leyendo y grabando un archivo Un simple separador Juntando las piezas Ordenando un TListView 3. ¿PASCAL ESTA MURIENDO? 4. UNA SIMPLE APLICACION DE LINEA DE ORDENES ________________________________________________________________________ 1. UNAS PALABRAS DEL EDITOR Estamos muy agradecidos por el apoyo, el aliento y las palabras de aprecio que hemos recibido de nuestros suscriptores. Estamos abiertos para oír sobre sus necesidades de programación para ver si podemos escribir artículos para cubrirlas en este boletín. Por favor recuerden que también se pueden suscribir gratuitamente a nuestras listas de correo en español para discutir los artículos de este boletín y otros temas de programación. Los ejemplos de código fuente de este boletín puede encontrarlos completos en nuestro sitio web. Saludos, Ernesto De Spirito eds2004 @ latiumsoftware.com ________________________________________________________________________ JfControls Lib. Multilenguaje. Multiapariencia. Skins. Privilegios. Más de 40 componentes integrados y personalizables. Múltiples problemas de programación resueltos. Administración centralizada de recursos. Para Delphi 3-7 y C++ Builder 3-6. http://www.jfactivesoft.com/spindex.htm ________________________________________________________________________ 2. BUSCADOR DE ARCHIVOS Alternativa para no subclasar y reemplazar un componente ======================================================== En el artículo "Subclasando componentes" del número anterior, vimos un ejemplo donde derivamos una nueva clase a partir de TListView para agregarle un nuevo método (GetItemAtX). Entonces instalamos este nuevo componente y cambiamos la clase de ListView1 en el formulario de nuestra aplicación de búsqueda de archivos. Cuando todo lo que hagamos sea simplemente agregar unos métodos a una clase, todo este procedimiento de tener que instalar un nuevo componente se puede evitar simplemente agregando la unidad de la nueva clase y castando el objeto cuando necesitemos llamar uno de los métodos que implementamos en esta nueva clase. Por ejemplo, en nuestro proyecto FileFind del segundo número del Delphi Newsletter, ahora le agregamos (Shift+F11) la unidad ListViewX que hemos escrito en el número anterior de este boletín, y únicamente cambiamos el evento doble-clic de ListView1: procedure TForm1.ListView1DblClick(Sender: TObject); var Col: Integer; ListItem: TListItem; begin ListItem := TListViewX(ListView1).GetItemAtX(Last.X, Last.Y, Col); if ListItem <> nil then begin if Col = 0 then begin if ShellExecute(Self.Handle, nil, PChar(ListItem.SubItems.Strings[0] + ListItem.Caption), nil, nil, SW_SHOWMAXIMIZED) <= 32 then begin Application.MessageBox(cstrCouldNotExecApp, 'Error', MB_ICONEXCLAMATION); end; // if end else if Col = 1 then begin if ShellExecute(Self.Handle, 'explore', PChar(ListItem.SubItems.Strings[0]), nil, nil, SW_SHOWMAXIMIZED) <= 32 then begin Application.MessageBox(cstrCouldNotExecApp, 'Error', MB_ICONEXCLAMATION); end; // if end; // if end; // if end; Es casi igual al que vimos en el número pasado, con excepción de la primera línea ejecutable. No podemos llamar ListView1.GetItemAtX(...) dado que GetItemAtX no es un método de TListView (la clase de ListView1) o uno de sus ancestros, sino un método de una clase derivada de TListView: TListViewX. Puesto que la clase derivada no agrega campos de datos (simplemente un de método), entonces ambas clases son compatibles de asignación y podemos castar un objeto TListView como un objeto TListViewX usando esta expresión: TListViewX(ListView1) que es de tipo TListViewX, así que la podemos usar para llamar al método GetItemAtX: TListViewX(ListView1).GetItemAtX(...) Leyendo y grabando un archivo ============================= En la aplicación de búsqueda de archivos, cargamos los contenidos de un archivo en un objeto TStringList usando el método LoadFromFile, y luego buscamos una frase en su propiedad Text. Esta forma de hacer las cosas no es realmente eficiente dado que LoadFromFile carga el archivo y lo analiza para separar las líneas, y luego cuando usamos la propiedad Text, un método interno es llamado para producir una cadena uniendo las líneas, de modo que este trabajo doble ha sido realizado para nada, además que estamos usando el más del doble del almacenamiento de memoria que necesitamos. Un enfoque mejor podría ser usar un objeto TFileStream (o los viejos procedimientos Assign, Reset, Read y Close) y directamente leer los contenidos de un archivo en una cadena. He aquí una función que usa TFileStream y que devuelve el contenido del archivo cuyo nombre se pasa como parámetro: function LoadFile(const FileName: string): string; var Stream: TFileStream; begin Stream := nil; Result := ''; try Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); SetLength(Result, Stream.Size); Stream.Read(Pointer(Result)^, Stream.Size); except Result := ''; Stream.Free; raise; end; // try Stream.Free; end; Si necesitamos guardar los contenidos de la cadena de vuelta al disco, podemos usar el siguiente procedimiento: procedure SaveFile(const FileName: string; content: string); var Stream: TFileStream; begin Stream := nil; try Stream := TFileStream.Create(FileName, fmCreate); Stream.Write(Pointer(content)^, Length(content)); except Stream.Free; raise; end; // try Stream.Free; end; Un simple separador =================== Como está ahora, nuestra aplicación busca archivos que contienen la frase ingresamos en el correspondiente cuadro de texto del formulario. Nuestra intención en realidad es que pueda buscar palabras, como un motor de búsqueda de la Internet. Por ejemplo, si ingresamos Hola Mundo en cuadro de texto, quisiéramos ver todos los archivos que contienen la palabra "Hola" y/o la palabra "Mundo" (ya sea juntas, separadas, cerca, lejos, en ese orden o que la segundo antes que la primera), y no todos los archivos que contenga la frase exacta "Hola Mundo", aunque a veces sí podemos querer realizar una búsqueda de frases... Entonces, lo que hacemos es definir una simple sintaxis para la búsqueda de palabras. Por ejemplo, asumimos que los espacios separan palabras, a excepción del texto encerrado por comillas: Hola Mundo ==> Dos palabras/frases: 'Hola' y 'Mundo' 'Hola' Mundo ==> Dos palabras/frases: 'Hola' y 'Mundo' Hola "Mundo" ==> Dos palabras/frases: 'Hola' y 'Mundo' "Hola" 'Mundo' ==> Dos palabras/frases: 'Hola' y 'Mundo' "Hola Mundo" ==> Una palabra/frase: 'Hola Mundo' 'Hola ' Mundo ==> Dos palabras/frases: 'Hola ' y 'Mundo' Hola " Mundo" ==> Dos palabras/frases: 'Hola' y ' Mundo' A veces necesitamos buscar caracteres especiales como tabulaciones, retornos de carro, avances de línea, etc. Normalmente no podemos meter algunos de estos caracteres en un cuadro de texto (o escribirlos en la línea de órdenes), pero podemos hacer como el preprocesador del lenguaje C y usar la barra invertida ("\") para indicar que el próximo carácter tiene un significado especial (por ejemplo \t representa el carácter de tabulación y \n representa el par CR+LF). Ahora, la primera cosa que vamos a hacer es escribir un simple separador de cadenas que separe las palabras de una cadena e interprete algunos caracteres especiales: procedure GetKeywords(StringList: TStringList; s: string); const blanks = [' ', #9, #13, #10]; var i, n: integer; c, quote: char; status: (tsBlank, tsToken); token: string; backslash: boolean; begin StringList.Clear; s := s + #0; // Añade marca de fin de cadena n := Length(s); quote := #0; // Para evitar advertencias del compilador backslash := false; // Para evitar advertencias del compilador status := tsBlank; for i := 1 to n do begin c := s[i]; case status of tsBlank: if c = #0 then begin break; // End of string end else if c in blanks then begin // Ignore blanks end else if c in ['"', ''''] then begin status := tsToken; quote := c; backslash := false; token := ''; // New token end else if c = '\' then begin status := tsToken; quote := #0; backslash := true; token := ''; // New token end else begin status := tsToken; quote := #0; backslash := false; token := c; // New token end; tsToken: if c = #0 then begin StringList.Add(Token); // Fin de la palabra break; // Fin de la cadena end else if (c in blanks) and (quote = #0) then begin StringList.Add(Token); // Fin de la palabra status := tsBlank; end else if backslash then begin case c of 'n': token := token + #13#10; 'r': token := token + #13; 'a': token := token + #10; 't': token := token + #9; 'q': token := token + '"'; '@'..'Z': token := token + Chr(Ord(c)-Ord('@')); 'e': token := token + #27; '/': token := token + #28; '*': token := token + #29; '-': token := token + #30; '+': token := token + #31; '^': token := token + #127; else token := token + c; end; // case backslash := false; end else if c = '\' then begin backslash := true; end else if c = quote then begin StringList.Add(Token); // Fin de la palabra status := tsBlank; end else begin token := token + c; end; end; // case status of end; // for end; Este procedimiento implementa una máquina de (dos) estados para hacer el trabajo de analizar la cadena que recibe como un parámetro para separar las palabras que se pone en una lista de cadenas (TStringList). Por ejemplo, si tenemos un objeto TStringList llamado SL1, después de la sentencia GetKeywords(SL1,'"\"Hola " Mundo"'), SL1 tendrá dos cadenas: SL1.Strings[0] (o simplemente SL1[0]) siendo '"Hola ' SL1.Strings[1] (o simplemente SL1[1]) siendo 'Mundo"' Piense en ella como una máquina expendedora a la que le puede meter caracteres como si fueran monedas. En realidad, en este caso los carac- teres se tomarán automáticamente desde una cadena dentro del bucle for..do. La máquina procesará cada carácter y cambia su estado interno como sea necesario. Por ejemplo, cuando su estado es tsBlank, si recibe más blancos (espacios, tabulaciones, CRs y LFs) se mantiene en ese estado, hasta que reciba un carácter diferente (el primero de una palabra) que la hace cambiar al estado tsToken, y comienza a "grabar" los próximos caracteres que llegan hasta que reciba un espacio blanco que señaliza el fin de la palabra. Es tiempo entonces para entregar el producto (agregar la palabra a la lista de cadenas) y retornar al estado tsBlank. Bueno, en realidad es un poco más complejo porque hemos considerado frases entrecomilladas, barras invertidas y un carácter de fin de entrada (#0), pero así y todo debería serle fácil de entender si está familiarizado con este tipo de algoritmos... Juntando las piezas =================== Ahora estamos listos para rescribir el procedimiento Ejecutar del hilo de búsqueda: procedure TThread1.Execute; var Keywords: TStringList; Content: string; i, n: integer; // ------------------------------------- procedure ScanFolder(const folder: string); var SearchRec: TSearchRec; i: integer; begin if FindFirst(folder + OwnerForm.Edit1.Text, faReadOnly Or faHidden Or faSysFile Or faArchive, SearchRec) = 0 then begin Location := folder; repeat try FileName := SearchRec.Name; Content := AnsiUpperCase(LoadFile(folder + FileName)); Score := 0; for i := 0 to n do if AnsiPos(Keywords[i], Content) <> 0 then inc(Score); if Score > 0 then begin inc(Count); Time := FileDateToDateTime(SearchRec.Time); Synchronize(AddFileName); end; // if except end; // try until Terminated Or (FindNext(SearchRec) <> 0); end; // if FindClose(SearchRec); if (not Terminated) and OwnerForm.Checkbox1.Checked then begin if FindFirst(folder + '*', faReadOnly Or faHidden Or faSysFile Or faArchive Or faDirectory, SearchRec) = 0 then begin repeat try if ((SearchRec.Attr and faDirectory) <> 0) and (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then ScanFolder(folder + SearchRec.Name + PathSeparator); except end; // try until Terminated Or (FindNext(SearchRec) <> 0); end; // if FindClose(SearchRec); end; // if end; // ------------------------------------- begin // procedure TThread1.Execute; Count := 0; Synchronize(Initialize); Keywords := TStringList.Create; GetKeywords(Keywords, OwnerForm.Edit2.Text); n := Keywords.Count - 1; for i := 0 to n do Keywords[i] := AnsiUpperCase(Keywords[i]); ScanFolder(OwnerForm.Edit3.Text); Keywords.Free; Synchronize(Finalize); end; Ordenando un TListView ====================== Decidimos agregar dos más columnas a ListView1: una para la marca de hora de los archivos y otra para el "puntaje" de los archivos, siendo éste el número de palabras que encontró en los archivos. Para poder llenar estas columnas, agregamos dos campos a la clase TThread1 para almacenar sus valores correspondientes: Time: TDateTime; // Marca de hora del archivo Score: cardinal; // Cantidad de palabras encontradas También tuvimos que agregar estas dos líneas al final del método AddFileName de TThread1: ListItem.SubItems.Add(DateTimeToStr(Time)); ListItem.SubItems.Add(IntToStr(Score)); Después de realizar la búsqueda, quisiéramos ordenar los archivos por su puntaje. Ordenar un TListView por su primera columna es fácil: estable- ciendo la propiedad SortType a stText es como establecer en True la propiedad Sorted de un objeto TListBox, y estableciendo SortType en stNone es como poner en False la propiedad Sorted de un objeto TListBox. Para ordenar un TListView por otra columna (o datos arbitrarios guar- dados o referenciados en los objetos TListItem), deberíamos o escribir un evento OnCompare o una función ordenadora para usarla con el método CustomSort. Si quiere mantener la lista ordenada mientras le agrega, modifica y borra elementos, entonces debería usar un evento OnCompare. El parámetro "Compare" debería establecerse a 1, - 1 o 0 dependiendo de si el primer elemento es mayor (o debería situarse después) que el segundo elemento, primer elemento es menor (o debería situarse antes) que el segundo elemento, o si los dos elemento son iguales, respectivamente. Por ejemplo: procedure TForm1.ListView1Compare(Sender: TObject; Item1, Item2: TListItem; Data: Integer; var Compare: Integer); var n1, n2: cardinal; begin n1 := StrToInt(Item1.SubItems[2]); n2 := StrToInt(Item2.SubItems[2]); if n1 > n2 then Compare := -1 else if n1 < n2 then Compare := 1 else Compare := 0; end; Si quiere mantener la lista ordenada, establezca su propiedad SortType en stBoth. Estableciendo SortType a stNone más tarde no deshará el ordenamiento, pero futuras adiciones y cambios hechos sobre la lista no se ordenarán, así que si sólo quiere o necesita realizar un ordenamiento temporal, puede hacer lo siguiente: ListView1.SortType := stBoth; ListView1.SortType := stNone; o sinó: ListView1.CustomSort(nil, 0); Si necesita un ordenamiento más veloz, entonces debería escribir una función ordenadora. Esta función debería devolver 1, - 1 o 0 (como el parámetro Compare del evento OnCompare que se discutió arriba). Por ejemplo: function ByScore(Item1, Item2: TListItem; Data: integer): integer; stdcall; var n1, n2: cardinal; begin n1 := StrToInt(Item1.SubItems[2]); n2 := StrToInt(Item2.SubItems[2]); if n1 > n2 then Result := -1 else if n1 < n2 then Result := 1 else Result := 0; end; Entonces, cada vez usted quiera ordenar la lista, llama a CustomSort pasándole la dirección de la función ordenadora. Por ejemplo: ListView1.CustomSort(@ByScore, 0); El parámetro Data del evento OnCompare es 0 si el suceso se llama automáticamente cuando SortType es stData o stBoth, pero si se genera a causa de un llamado a CustomSort, entonces su valor es el segundo parámetro que se pasa a este método. Lo mismo sucede con el parámetro Data de la función ordenadora, de modo que el parámetro Data se usa normalmente para especificar la columna por la que se ordenará (no lo usamos en nuestro ejemplo pues siempre ordenamos por la cuarta columna). En nuestro ejemplo agregamos la función ByScore como está presentada arriba y al final del método Finalize agregamos esta sentencia: OwnerForm.ListView1.CustomSort(@ByScore, 0); Eso es todo por ahora. Puede encontrar el código fuente en nuestro sitio web: http://www.latiumsoftware.com/descarga/p0002.zip ________________________________________________________________________ 3. ¿PASCAL ESTA MURIENDO? Una línea de discusión bajo este título tuvo lugar hace más de una semana en la lista de correo fpc-pascal de Free Pascal. Es cierto que C++ es todavía el lenguaje top en casi todos los sistemas operativos, que Visual Basic tiene una posición prominente en la plataforma Windows, y que otros lenguajes como Java, Perl, etc. están tomando la Internet, pero eso no significa necesariamente que Pascal esté muriendo. Delphi le brinda a los programadores un desempeño comparable a cualquier compilador C con la fácil sintaxis del lenguaje Pascal, a veces hasta compila más rápido (una sintaxis más fácil ayuda y también el empaque de unidades en vez de bibliotecas y archivos cabecera), viene con una biblioteca importante de componentes visuales y no visuales que uno puede fácilmente usar en sus aplicaciones (a los que puede agregar centenares de componenes que uno puede bajar de la red, desde freeware hasta comercial, incluyendo muchos que son open source) y tiene una de las comunidades de programadores más fuertes, sólidas y serviciales. Una vez que uno comienza a habituarse a Delphi y lo compara con otras herramientas, comienza uno a entender qué significa la sigla RAD! (Rapid Application Development, Desarrollo Rápido de Aplicaciones). No por nada Delphi ha salvado a Borland de la quiebra y todavía vende copias. También una nueva versión está actualmente en desarrollo para Windows conjuntamente con su contraparte para Linux, llegando así a convertirse en una de las primeras herramientas RAD comerciales para ese S.O. como dijimos en el primer número de nuestro Kylix Newsletter. Ya hay otros herramientas RAD comerciales para Linux, así que estrictamente hablando Delphi no será la primera, pero muchos pronostican que será el primero en llegar a ser ampliamente aceptado (por lo menos tiene todo el potencial). No sería descabellado pensar que Kylix puede ayudar a Linux a crecer en cantidad y calidad de aplicaciones, permitiéndole seguir ganando terreno no sólo en el mercado de servidores de internet donde ya logró una posición importante debajo de Windows NT, sino también en el mercado de servidores de negocios donde tiene una posición muy pobre y hasta ahora ha probado ser incapaz de cambiar mucho esa situación (aunque de todos modos crece, pero a un ritmo muy lento). A su vez, céteris paribus (especialmente considerando que otros jugadores grandes no entren en el juego), el crecimiento de Linux en este mercado podría retroalimentar el crecimiento de Kylix en una forma de de relación simbiótica. Pero toda esto es simplemente especulación... Y Delphi no es la única implementación del lenguaje Pascal que hay dando vuelta. Además de otros compiladores e intérpretes pascal, Free Pascal es un interesante proyecto open source que ambiciona llevar a Pascal a otros microprocesadores y sistemas operativos. El movimiento del software libre se va haciendo más importante cada día, la evangelización continúa y no sabemos cuáles son sus límites, pero ciertamente sabemos que no los ha alcanzado aún... ¿Concuerda? ¿Disiente? ¿Tiene comentarios? ¿Opiniones? ¿Le gustaría compartirlas con nosotros? ¡Por favor escríbanos! ________________________________________________________________________ 4. UNA SIMPLE APLICACION DE LINEA DE ORDENES Nos han pedido más ejemplos de TStringList y un ejemplo de una aplicación en línea de órdenes (consola), así que aquí van dos por el precio uno! :) Decidimos escribir una aplicación capaz de reemplazar múltiples ocurrencias de una de cadena por otra cadena, en un archivo o un grupo de archivos. Por ejemplo: Replacer "e-mail" "email" c:\web\index.htm c:\web\content\*.html @c:\web\filelist.txt Esto buscará index.htm y todos los archivos que concuerdan con la especificación de archivo c:\web\content\*.html, y todos los archivos listados en las líneas del archivo c:\web\filelist.txt (se aceptan comodines), buscando el texto "e-mail" y reemplazando todos las ocurrencias que se encuentren con "email". Replacer @searchandreplace.txt c:\web\index.htm c:\web\content\*.html @c:\web\filelist.txt Esto buscará los mismos archivos detallados arriba, pero un archivo llamado searchandreplace.txt puede contener múltiples cadenas de búsqueda y reemplazo (un par por línea), por ejemplo: "</tr>" "" "</font></td>" "</td>" "\t" " " Esto eliminaría todas las ocurrencias de "</tr>" y todas las ocurrencias de "</font>" que preceden un "</td>". También, todas las tabulaciones serán reemplazadas por espacios. Para evitar reinventar la rueda, reutilizaremos GetKeywords, LoadFile y SaveFile que implementamos arriba, así que las pusimos en una unidad llamada Common. Aquí es el resto de la aplicación: program Replacer; {$APPTYPE CONSOLE} {$DEFINE Debug} uses SysUtils, Classes, Common; var Search, Replace, Files, Temp: TStringList; ff, i, j, m, n: integer; SearchRec: TSearchRec; s: string; FilePath, FileName: string; begin WriteLn('Replacer v1.0 - Copyright (c) 2000 Ernesto De Spirito'); if (ParamCount <= 1) or ((ParamCount = 2) and (Copy(ParamStr(1),1,1) <> '@')) then begin Write('Searches a file (or a group of files) for'); WriteLn(' the occurrences of one string (or a'); Write('group of strings) and replaces them for'); WriteLn(' another string (or a group of strings).'); WriteLn; WriteLn('Syntax:'); Write(#9'Replacer "search text"') WriteLn(' "replace text" "filename.ext"'); Write(#9'Replacer "search text"'); WriteLn( "replace text" "@filename.ext"'); WriteLn(#9'Replacer "@searchandreplace.ext" "filename.ext"'); WriteLn(#9'Replacer "@searchandreplace.ext" "@filename.ext"'); WriteLn; Write('filename.ext is the full path name of'); WriteLn(' the file(s) to process. Wildcards are'); Write('accepted. Multiple files can be specified'); WriteLn(' adding extra parameters.'); Write('The at sign ("@") before a filename to'); WriteLn(' process indicates the specified file'); Write('contains a list of filenames (one per'); WriteLn(' line, and can include wildcards).'); Write('The at sign ("@") before the first parameter'); WriteLn(' indicates a file containing'); Write('search and replace strings (should be enclosed'); WriteLn(' by single or double quotes).'); WriteLn; Write('WARNING: Contents of the files will'); WriteLn(' be changed. Use at your onw risk!'); end else begin // Initialize variables to avoid compiler warnings Search := nil; Replace := nil; Files := nil; Temp := nil; try // Create the objects Search := TStringList.Create; // List of search strings Replace := TStringList.Create; // List of replace strings Files := TStringList.Create; // List of files to process Temp := TStringList.Create; // Temporal string list // Get search and replace strings if Copy(ParamStr(1),1,1) <> '@' then begin ff := 3; // First filename // Easy case. Only one search and replace string Search.Add('"' + ParamStr(1) + '" "' + ParamStr(2) + '"'); end else begin ff := 2; // First filename // Read the file with the search and replace strings. Search.LoadFromFile(Copy(ParamStr(1),2, Length(ParamStr(1))-1)); end; // if // Separate and parse the strings m := Search.Count - 1; j := 0; while j <= m do begin if Trim(Search[j]) = '' then begin Search.Delete(j); dec(m); end else begin GetKeywords(Temp, Search[j]); Search[j] := Temp[0]; Replace.Add(Temp[1]); inc(j); end; end; // while // Ok, now lets get the files to process n := ParamCount; for i := ff to n do begin if Copy(ParamStr(i),1,1) <> '@' then begin Files.Add(ParamStr(i)); end else begin Temp.LoadFromFile(Copy(ParamStr(i),2, Length(ParamStr(i))-1)); Files.AddStrings(Temp); Temp.Clear; end; end; // Trim file names and delete empty lines n := Files.Count - 1; for i := n downto 0 do begin s := Trim(Files[i]); if s = '' then begin Files.Delete(i); end else begin Files[i] := s; end; end; // for // And now we process the files... n := Files.Count - 1; for i := 0 to n do begin // For each filespec if FindFirst(Files[i], faArchive, SearchRec) = 0 then begin FilePath := ExtractFilePath(Files[i]); repeat // Process each file in a filespec FileName := FilePath + SearchRec.Name; Write(FileName + '...'); try // Reads the file s := LoadFile(FileName); // Do the search and replace thing for j := 0 to m do begin s := StringReplace(s, Search[j], Replace[j], [rfReplaceAll, rfIgnoreCase]); end; // for // And writes the file to disk SaveFile(FileName, s); WriteLn(' Ok!'); except on e: exception do WriteLn(' Error: ' + e.Message); end; until FindNext(SearchRec) <> 0; FindClose(SearchRec); end; // if end; //for (processing files) except on e: Exception do WriteLn('Error: ', e.Message); end; // try Search.Free; Replace.Free; Files.Free; Temp.Free; end; WriteLn; {$IFDEF Debug} Write('Press <ENTER> to end...'); ReadLn; {$ENDIF Debug} end. El código fuente completo puede encontrarlo en nuestro sitio web: http://www.latiumsoftware.com/descarga/p0002.zip Si necesita alguna ayuda puede unirse sin cargo a la lista de correo en español delphi-intermedio, donde otros suscriptores del boletín podrían ser capaces de ayudarle y responder a sus preguntas y dudas. Quienes programen en Free Pascal podrían tener que adaptar el código un poco, pero la mayoría de las cosas que usamos en este ejemplo están disponibles en Free Pascal. Estamos buscando alguien que nos pueda ayudar a modificar ejemplos como este para trabajar con Free Pascal bajo Windows y Linux. Si usted conoce Free Pascal lo suficientemente bien para hacerlo y tiene algún tiempo para esto, por favor contáctenos. Si usted tiene aun más tiempo y quiere escribir algunos artículos introduciendo Free Pascal (Windows y Linux) para ser incluidos en este boletín, POR FAVOR CONTACTENOS! :-) ________________________________________________________________________ Si no has recibido el archivo con el código fuente completo de los ejemplos que se presentan en este boletín, puedes descargarlo de la siguiente dirección: http://www.latiumsoftware.com/descarga/p0002.zip ________________________________________________________________________ Página principal: http://www.latiumsoftware.com/es/pascal/index.php Página del grupo: http://espanol.groups.yahoo.com/group/boletin-pascal/ Para suscribirse / apuntarse: boletin-pascal-subscribe@gruposyahoo.com Para cancelar / removerse: boletin-pascal-unsubscribe@gruposyahoo.com Para reportar problemas con la suscripción: eds2004 @ latiumsoftware.com ________________________________________________________________________ Este boletín se provee "TAL Y COMO ESTA", sin garantía de ninguna clase. Su uso implica la aceptación de nuestros términos de licencia y de la ausencia de garantía que puedes leer en nuestro sitio web. Allí también encontrarás una nota sobre marcas registradas. Te animamos a que redis- tribuyas este boletín, siempre y cuando lo hagas en forma completa (incluyendo la información de copyright), sin modificaciones y de manera gratuita. Los artículos son copyright de sus respectivos autores y se reproducen aquí con el permiso de los mismos. ________________________________________________________________________ Latium Software http://www.latiumsoftware.com/es/index.php Copyright (c) 2000 por Ernesto De Spirito. Todos los derechos reservados ________________________________________________________________________ |
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
¿Errores? ¿Omisiones? ¿Comentarios? Por favor contáctanos!






