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
eds2008 @ 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-2006 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/es/file.php?id=p02
________________________________________________________________________
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/es/file.php?id=p02
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/es/file.php?id=p02
________________________________________________________________________
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: eds2008 @ 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
________________________________________________________________________
|