17 agosto 2007

Convertir una variable String a otro tipo

Delphi posee un amplio repertorio de funciones para convertir una cadena de texto a otro tipo de variable. Veamos de que funciones disponemos:

function StringToWideChar( const Source: string; Dest: PWideChar; DestSize: Integer ): PWideChar;

Esta función devuelve la cadena Source en formato de caractéres unicode. Por ejemplo:

var
sTexto: String;
PWS: PWideChar;
begin
sTexto := 'Convirtiendo texto a caracteres Unicode (16 bits)';
New( PWS );
StringToWideChar( sTexto, PWS, Length( sTexto ) + 1 );
ShowMessage( PWS );
Dispose( PWS );
end;

function StrToCurr( const S: string ): Currency; overload;

Convierte una cadena de texto S a un valor Currency. Por ejemplo:

StrToCurr( '1234,5678' ) devuelve 1234,5678
StrToCurr( '1234,,5678' ) provoca el error: '1234,,5678' is not a valid floating point value

function StrToCurrDef( const S: string; const Default: Currency ): Currency; overload;

Esta función es igual que StrToCurr aceptando además como segundo parámetro el valor que deseamos que tenga por defecto en el caso de que falle la conversión. Por ejemplo:

StrToCurrDef( '1234,5678', 0 ) devuelve 1234,5678
StrToCurrDef( '1234,,5678', 0 ) devuelve 0 y no provoca ningún error

function StrToDate( const S: string ): TDateTime; overload;

Convierte la cadena de texto S a un valor de fecha. Por ejemplo:

StrToDate( '17/06/2007' ) devuelve 17/06/2007
StrToDate( '17-06-2007' ) provoca el error: '17-06-2007' is not valid date

function StrToDateDef( const S: string; const Default: TDateTime ): TDateTime; overload;

Esta función es igual que StrToDate aceptando además como segundo parámetro el valor que deseamos que tenga por defecto en el caso de que falle la conversión. Por ejemplo:

StrToDateDef( '17/06/2007', StrToDate( '1/1/2007' ) ) devuelve 17/06/2007
StrToDateDef( '17-06-2007', StrToDate( '1/1/2007' ) ) devuelve 01/01/2007 y no provoca ningún error
StrToDateDef( '17/15/2007', StrToDate( '1/1/2007' ) ) devuelve 01/01/2007 y no provoca ningún error

function StrToDateTime( const S: string ): TDateTime; overload;

Convierte la cadena de texto S a un valor de fecha y hora. Por ejemplo:

StrToDateTime( '17/06/2007 12:25' ) devuelve 17/06/2007 12:25:00
StrToDateTime( '17-06-2007 12:25' ) provoca el error: '17-06-2007 12:25' is not a valid date an time

function StrToDateTimeDef( const S: string; const Default: TDateTime ): TDateTime; overload;

Esta función es igual que StrToDateTime aceptando además como segundo parámetro el valor que deseamos que tenga por defecto en el caso de que falle la conversión. Por ejemplo:

StrToDateTimeDef( '17/06/2007 12:25', StrToDateTime( '1/1/2007 0:0:0' ) ) devuelve 17/06/2007 12:25:00
StrToDateTimeDef( '17-06-2007 12:25', StrToDateTime( '1/1/2007 0:0:0' ) ) devuelve 01/01/2007 12:00:00 sin errores
StrToDateTimeDef( '17/15/2007 12:25', StrToDateTime( '1/1/2007 0:0:0' ) ) devuelve 01/01/2007 12:00:00 sin errores

function StrToFloat( const S: string ): Extended; overload;

Convierte la cadena de texto S a un valor Extended. Por ejemplo:

StrToFloat( '1234,56789' ) devuelve 1234,56789
StrToFloat( '1234,,56789' ) provoca el error: '1234,,56789' is not valid floating point value

function StrToFloatDef( const S: string; const Default: Extended ): Extended; overload;

Esta función es igual que StrToFloat aceptando además como segundo parámetro el valor que deseamos que tenga por defecto en el caso de que falle la conversión. Por ejemplo:

StrToFloatDef( '1234,56789', 0 ) devuelve 1234,56789
StrToFloatDef( '1234,,56789', 0 ) devuelve 0 y no provoca ningún error

function StrToInt( const S: string ): Integer;

Convierte la cadena de texto S a un valor Integer (permite caracteres hexadecimales). Por ejemplo:

StrToInt( '1234' ) devuelve 1234
StrToInt( '$FFFF' ) devuelve 65535
StrToInt( 'FFFF' ) provoca el error: 'FFFF' is not a valid integer value

function StrToIntDef( const S: string; const Default: Integer ): Integer;

Esta función es igual que StrToInt aceptando además como segundo parámetro el valor que deseamos que tenga por defecto en el caso de que falle la conversión. Por ejemplo:

StrToIntDef( '1234', 0 ) devuelve 1234
StrToIntDef( '$FFFF', 0 ) devuelve 65535
StrToIntDef( 'FFFF', 0 ) devuelve 0 y no provoca ningún error
StrToIntDef( '', 0 ) devuelve 0 y no provoca ningún error

function StrToInt64( const S: string ): Int64;

Convierte la cadena de texto S a un valor Int64 (permite caracteres hexadecimales). Por ejemplo:

StrToInt64( '12345678' ) devuelve 12345678
StrToInt64( '$FFFFFFFF' ) devuelve 4294967295

function StrToInt64Def( const S: string; Default: Int64 ): Int64;

Esta función es similar a StrToInt64 aceptando además como segundo parámetro el valor que deseamos que tenga por defecto en el caso de que falle la conversión. Por ejemplo:

StrToInt64Def( '12345678', 0 ) devuelve 12345678
StrToInt64Def( '$FFFFFFFF', 0 ) devuelve 4294967295
StrToInt64Def( 'FFFFFFFF', 0 ) devuelve 0 y no provoca níngun error
StrToInt64Def( '', 0 ) devuelve 0 y no provoca níngun error

function StrToTime( const S: string ): TDateTime; overload;

Convierte la cadena de texto S a un valor TTime o TDateTime. Por ejemplo:

StrToTime( '12:32:54' ) devuelve 12:32:54
StrToTime( '12:32' ) devuelve 12:32:00
StrToTime( '12:32:67' ) provoca el error: '12:32:67' is not a valid time

function StrToTimeDef( const S: string; const Default: TDateTime ): TDateTime; overload;

Esta función es igual a StrToTime aceptando además como segundo parámetro el valor que deseamos que tenga por defecto en el caso de que falle la conversión. Por ejemplo:

StrToTimeDef( '12:32:54', StrToTime( '00:00:00' ) ) devuelve 12:32:54
StrToTimeDef( '12:32', StrToTime( '00:00:00' ) ) devuelve 12:32:00
StrToTimeDef( '12:32:67', TimeToStr( '00:00:00' ) ) devuelve 0:00:00 y no provoca ningún error

function StrToBool( const S: string ): Boolean;

Convierte la cadena de texto S a un valor Boolean. Por ejemplo:

StrToBool( 'True' ) devuelve True
StrToBool( 'False' ) devuelve False
StrToBool( 'Fals' ) provoca el error: 'Fals' is not valid boolean value

function StrToBoolDef( const S: string; const Default: Boolean ): Boolean;

Esta función es igual a StrToBool aceptando además como segundo parámetro el valor que deseamos que tenga por defecto en el caso de que falle la conversión. Por ejemplo:

StrToBoolDef( 'True', False ) devuelve True
StrToBoolDef( 'False', False ) devuelve False
StrToBoolDef( 'Tru', False ) devuelve False y no provoca ningún error
StrToBoolDef( 'Fals', False ) devuelve False y no provoca ningún error

procedure Val( S; var V; var Code: Integer );

Este procedimiento convierte la cadena de texto S a un valor numérico. Si la conversión falla nos devuelve en la variable Code la posición en la cadena de texto S donde está el error. Por ejemplo:

var
s: String;
e: Extended;
begin
s := '1234.5678';
Val( s, e, iCodigo );

if iCodigo = 0 then
ShowMessage( 'Conversión correcta: ' + FloatToStr( e ) )
else
ShowMessage( 'Error en la posición: ' + IntToStr( iCodigo ) )
end;

Al ejecutar este bloque de código devolverá:

Conversion correcta: 1234,5678

Pero si sustituimos la primera línea por:

s := '1234,5678';

Entonces mostrará el mensaje:

Error en la posición 5.

Aquí hay que tener mucho cuidado, porque al contrario que la función FloatToStr si ponemos la notación decimal igual que la tenemos en Windows provocará un error. En este caso la notación correcta es el punto y no la coma (se podría solucionar cambiando la variable DecimalSeparator).

Pruebas realizadas en Delphi 7.

16 agosto 2007

El objeto StringList (y III)

IMPORTAR Y EXPORTAR ELEMENTOS DE LA LISTA

Se pueden guardar los elementos de la lista en un archivo de texto utilizando para ello el método SaveToFile:

procedure SaveToFile( const FileName: string ); virtual;

Por ejemplo vamos a guardar nuestra lista de clientes en el mismo directorio donde ejecutamos el programa:

Clientes.SaveToFile( ExtractFilePath( Application.ExeName ) + 'clientes.txt' );

Si más adelante deseamos recuperar el listado utilizamos el procedimiento:

procedure LoadFromFile( const FileName: string ); virtual;

Por ejemplo:

Clientes.LoadFromFile( ExtractFilePath( Application.ExeName ) + 'clientes.txt' );

Si tenemos dos listas en memoria también podemos copiar elementos de una lista a otra. Por ejemplo:

var
Animales, Animales2: TStringList;
begin
Animales := TStringList.Create;
Animales2 := TStringList.Create;

Animales.Add( 'PERRO' );
Animales.Add( 'GATO' );
Animales.Add( 'LEÓN' );

Animales2.Add( 'VACA' );
Animales2.Add( 'PATO' );
Animales2.Add( 'ÁGUILA' );

// Añadimos a la lista de ANIMALES el contenido de ANIMALES2 y ordenamos la lista
Animales.AddStrings( Animales2 );
Animales.Sort;

// Mostramos el resultado en un campo Memo
Memo.Lines.Add( Animales.Text );

Animales2.Free;
Animales.Free;
end;

El resultado de la lista Animales después de ordenarla sería:

ÁGUILA
GATO
LEÓN
PATO
PERRO
VACA

Otra de las cosas que se pueden hacer es asignar los elementos de una lista a otra, eliminando los elementos de la lista original (sería algo así como copiar y pegar). Por ejemplo si queremos sustituir todos los elementos de la lista Animales por los de Animales2 habría que hacer:

Animales.Assign( Animales2 );

lo cual elimina todos los elementos de la lista Animales e inserta todos los de Animales2.

LISTAS ORDENADAS AUTOMÁTICAMENTE

Anteriormente vimos como una lista podía ordenarse con el método Sort, pero resulta algo molesto y lento tener que volver a ordenar la lista cada vez que se inserta, modifica o elimina un elemento de la misma. Para evitar eso los objetos StringList disponen de la propiedad booleana Sorted la cual una vez esta activa ordenará todos los elementos de la lista automáticamente independientemente de las operaciones que se realicen en la misma.

Vamos a crear una nueva lista ordenada:

var
Vehiculos: TStringList;
begin
Vehiculos := TStringList.Create;
Vehiculos.Sorted := True;
Vehiculos.Add( 'RENAULT' );
Vehiculos.Add( 'PEUGEOT' );
Vehiculos.Add( 'MERCEDES' );
Memo.Lines.Add( Vehiculos.Text );
Vehiculos.Free;
end;

Al ejecutar este código mostrará en el campo Memo los elemento ya ordenados:

MERCEDES
PEUGEOT
RENAULT

Tanto si insertamos o eliminamos elementos, la lista seguirá ordenada:

Vehiculos.Delete( 3 );
Vehiculos.Add( 'BMW' );

Quedaría se la siguiente manera:

BMW
MERCEDES
PEUGEOT

Lo que no se puede hacer en una lista ordenada es modificar sus elementos:

Vehiculos[1] := 'AUDI';

Ya que provocaría el error:

Operation not allowed on sorted list (operación no permitida en una lista ordenada).

Para ello habría que desconectar la propiedad Sorted, modificar el elemento y volver a activarla:

Vehiculos.Sorted := False;
Vehiculos[1] := 'AUDI';
Vehiculos.Sorted := True;

Ahora si tendríamos el efecto deseado:

AUDI
BMW
PEUGEOT

Otra de las operaciones que no se pueden hacer en una lista ordenada es añadir elementos duplicados. En el caso anterior si hacemos:

Vehiculos.Add( 'BMW' )

no hará absolutamente nada. Para poder añadir elementos duplicados en una lista ordenada hay que modificar la propiedad Duplicates la cual puede contener los siguientes valores:

dupIgnore -> Ignora el añadir elementos duplicados (así es como está por defecto)
dupAccept -> Acepta elementos duplicados
dupError -> Da un error al añadir elementos duplicados

Si hacemos lo siguiente:

Vehiculos.Duplicates := dupAccept;
Vehiculos.Add( 'BMW' );

Entonces si funcionará correctamente:

AUDI
BMW
BMW
PEUGEOT

Pero si activamos:

Vehiculos.Duplicates := dupError;
Vehiculos.Add( 'BMW' );

Nos mostrará el mensaje:

String list does not allow duplicates (StringList no acepta duplicados).

ASOCIANDO OBJETOS A LOS ELEMENTOS DE UNA LISTA

Una de las características más importantes de una lista TStringList es la de asociar a cada elemento de la lista la instacia de un objeto creado. Vamos a ver un ejemplo donde voy a crear la clase TProveedor y voy a utilizarla para crear una lista de proveedores donde cada elemento tiene su propio objeto:

type
TProveedor = class
sNombre: String;
cSaldo: Currency;
bPagado: Boolean;
end;

var
Proveedores: TStringList;
Proveedor: TProveedor;
begin
Proveedores := TStringList.Create;

Proveedor := TProveedor.Create;
with Proveedor do
begin
sNombre := 'SUMINISTROS PALAZÓN';
cSaldo := 1200.24;
bPagado := False;
end;

Proveedores.AddObject( 'B78234539', Proveedor );

Proveedor := TProveedor.Create;
with Proveedor do
begin
sNombre := 'ELECTRIDAUTON, S.L.';
cSaldo := 2460.32;
bPagado := True;
end;

Proveedores.AddObject( 'B98654782', Proveedor );

Proveedor := TProveedor.Create;
with Proveedor do
begin
sNombre := 'FONTANERIA MARTINEZ, S.L.';
cSaldo := 4800.54;
bPagado := False;
end;

Proveedores.AddObject( 'B54987374', Proveedor );
end;

Hemos creado una lista de proveedores e instanciado tres proveedores asociandolos a la lista mediante el C.I.F. del proveedor. Ahora bien, ¿como accedemos a cada proveedore de la lista? Pues muy fácil. El objeto StringList dispone de la propiedad Objects la cual nos devuelve la referencia a un objeto en cuestión. Por ejemplo vamos a buscar a FONTANERIA MARTINEZ por su C.I.F.:

var
iProveedor: Integer;
begin
iProveedor := Proveedores.IndexOf( 'B54987374' );

if iProveedor > -1 then
begin
Proveedor := Proveedores.Objects[iProveedor] as TProveedor;
Memo.Lines.Add( '' );
Memo.Lines.Add( 'BUSCANDO AL PROVEEDOR: B54987374' );
Memo.Lines.Add( '' );
Memo.Lines.Add( 'NOMBRE: ' + Proveedor.sNombre );
Memo.Lines.Add( 'SALDO: ' + CurrToStr( Proveedor.cSaldo ) );
Memo.Lines.Add( 'PAGADO: ' + BoolToStr( Proveedor.bPagado, True ) );
end;
end;

El resultado sería:

BUSCANDO AL PROVEEDOR: B54987374

NOMBRE: FONTANERIA MARTINEZ, S.L.
SALDO: 4800,54
PAGADO: False

Esto es algo mucho más útil que utilizar un array:

var
Proveedores: array of TProveedor;
....

ya que es mucho más flexible y no hay que preocuparse por los huecos al añadir o eliminar registros. Esto nos permite volcar información de registros de la base de datos a memoria mapeando el contenido de un registro (Clientes, Facturas, etc) dentro de su clase correspondiente (Por ejemplo: se podrían cargar una cantidad indeterminada de albaranes del mismo cliente para sumarlos y pasarlos a factura).

Para añadir objetos a un StringList aparte de la función AddObject se pueden insertar objetos mediante el procedimiento:

procedure InsertObject( Index: Integer; const S: string; AObject: TObject ); override;

el cual inserta el elemento en la posición que deseemos desplazando el resto de la lista. Otra de las ventajas de asociar objetos a un StringList en vez de utilizar un array es que al liberarlo de memoria también elimina cada instancia de objeto asociado a cada elemento de la lista:

Proveedores.Free;

no teniendo así que preocuparnos con errores de liberación de memoria.

Con esto terminados de vez las características más importantes de la clase TStringList.

Pruebas realizadas en Delphi 7.

15 agosto 2007

El objeto StringList (II)

Vamos a seguir viendo las características de la clase TStringList.

ELIMINANDO ELEMENTOS DE LA LISTA

Con el método Delete se elimina un elementos de la lista la cual vuelve a reagrupar sus elemento asignando un nuevo índice. Actualmente la lista de clientes contiene:

CARLOS ABENZA MARTINEZ
PASCUAL CAMPOY FERNANDEZ
MARIA PALAZÓN PÉREZ
PEDRO SANCHEZ GARCIA
ROSA GUILLÉN LUNA
LUIS GARCIA ROJO
MANUEL ESCUDERO BERNAL

Vamos a eliminar a ROSA de la lista:

Clientes.Delete( 4 );

Ahora el cliente LUIS tiene un índice de 4 (en vez de 5 como antes), es decir, al contrario de un array dinámico, si en un objeto StringList se elimina un elemento de la lista no queda ningún hueco, ya que vuelven a agruparse sus elementos.

Un error común para eliminar todos los elementos de la lista sería hacer lo siguiente:

var
i: Integer;
begin
for i := 0 to Clientes.Count - 1 do
Clientes.Delete( i );
end;

Nos daría el error:

List index out of bounds(3)

¿Por qué ocurre esto? Pues por la sencilla razón de que cada que se elimina un elemento, al reagruparse el resto de los elementos se va también disminuyendo el número de los mismos y sus índices, con lo cual cuando intenta acceder a un elemento que ya no existe provoca un error. La manera correcta de hacerlo sería:

var
i: Integer;
begin
for i := 0 to Clientes.Count - 1 do
Clientes.Delete( 0 );
end;

Como vemos arriba se elimina sólo el primer elemento y como se van eliminando como si fuera una pila de platos al final desaparecen todos. Naturalmente una clase StringList dispone del método Clear que elimina todos los elementos de la lista:

Clientes.Clear;

MOVIENDO ELEMENTOS EN LA LISTA

Supongamos que tenemos la anterior lista de clientes:

CARLOS ABENZA MARTINEZ
PASCUAL CAMPOY FERNANDEZ
MARIA PALAZÓN PÉREZ
PEDRO SANCHEZ GARCIA
LUIS GARCIA ROJO
MANUEL ESCUDERO BERNAL

Vamos a mover a MARIA a la primera posición de la lista:

Clientes.Move( 2, 0 );

El comando Move acepta como primer parámetro en que posición esta el elemento que deseamos mover y como segundo parámetro su destino. En el caso anterior llevamos a MARIA de la posición 2 a la 0:

MARIA PALAZÓN PÉREZ
CARLOS ABENZA MARTINEZ
PASCUAL CAMPOY FERNANDEZ
PEDRO SANCHEZ GARCIA
LUIS GARCIA ROJO
MANUEL ESCUDERO BERNAL

Ahora bien, no hay que confundirse el mover elementos en la lista con intercambiarlos. El mover un elemento sólo lo elimina de la posición actual y lo lleva a la posición deseada, pero mantiene el orden natural de la lista. Si lo que queremos es intercambiar dos elementos para ello disponemos del método Exchange el cual tiene los mismos parámetros que Move salvo que lo que hace es intercambiar las posiciones de ambos elementos.

Vamos a ver como cambiar la posición de MANUEL (que es la 5) con la de CARLOS (que es la 1):

Clientes.Exchange( 5, 1 );

Su resultado sería:

MARIA PALAZÓN PÉREZ
MANUEL ESCUDERO BERNAL
PASCUAL CAMPOY FERNANDEZ
PEDRO SANCHEZ GARCIA
LUIS GARCIA ROJO
CARLOS ABENZA MARTINEZ


BUSCANDO ELEMENTOS EN LA LISTA

Se puede averiguar la posición de un elemento en la lista con con los métodos IndexOf e IndexOfName:

function IndexOf( const S: string ): Integer; override;
function IndexOfName(const Name: string): Integer; virtual;


El método IndexOf toma como parámetro el nombre del elemento que se desea buscar y devuelve su índice si lo encuentra (el primer elemento es el cero) y -1 si no lo encuentra. Por ejemplo:

Clientes.IndexOf( 'PEDRO SANCHEZ GARCIA' ) devuelve 3
Clientes.IndexOf( 'PEDRO SANCHEZ' ) devuelve -1

La función IndexOf sólo busca el primer elemento encontrado en la lista, de modo que si hay dos elementos iguales sólo muestra el índice del primero (de ahí la importancia de tener la lista ordenada).

Después tenemos la función IndexOfName destinada a buscar elementos de tipo NOMBRE = VALOR como vimos anteriormente con el diccionario:

Diccionario.IndexOfName( 'BOOK' ) devuelve 0
Diccionario.IndexOfName( 'MOUSE' ) devuelve 1
Diccionario.IndexOfName( 'TABLE' ) devuelve 2

Por último tenemos la función Find utilizada para buscar elementos sólo si la lista esta ordenada. Por ejemplo:

var
iPosicion: Integer;
bEncontrado: Boolean;
begin
Clientes.Sort; // ordenamos la lista
bEncontrado := Clientes.Find( 'PEDRO SANCHEZ GARCIA', iPosicion );
end;

La función Find es similar a función IndexOf añadiendo como segundo parámetro una variable donde depositar la posición del elemento buscado y cuyo valor devuelto en la función será True o False dependiendo si ha encontrado el elemento en la lista.

Después de ejecutar este código la lista quedaría de la siguiente manera:

CARLOS ABENZA MARTINEZ
LUIS GARCIA ROJO
MANUEL ESCUDERO BERNAL
MARIA PALAZÓN PÉREZ
PASCUAL CAMPOY FERNANDEZ
PEDRO SANCHEZ GARCIA

La variable bEncontrado valdría True y iPosicion sería igual a 5.

En el próximo artículo terminaremos de ver todas las funcionalidades de un StringList.

Pruebas realizadas en Delphi 7.

14 agosto 2007

El objeto StringList (I)

Un objeto de la clase TStringList es muy útil para procesar listas de cadenas de texto en memoria. Cada elemento de la lista puede ordenarse, insertar nuevos elementos, eliminar otros, etc.

La lista pueder ser construida de vacío o bien se pueden cargar los elementos desde un archivo de texto separando cada elemento por comas.

La clase TStringList hereda de la clase TStrings, aunque no se recomienda esta última por ser algo incompleta.

CREANDO UNA LISTA

Veamos un sencillo ejemplo para crear una lista de clientes:

var Clientes: TStringList;
begin
Clientes := TStringList.Create;
Clientes.Add( 'PEDRO SANCHEZ GARCIA' );
Clientes.Add( 'MARIA PALAZÓN PÉREZ' );
Clientes.Add( 'CARLOS ABENZA MARTINEZ' );
Clientes.Add( 'ANA GONZALEZ RUIZ' );
end;

ACCEDIENDO A LOS ELEMENTOS DE LA LISTA

A los elementos de la lista se accede como si fuera un array dinámico (el primer elemento es el 0). Si escribimos:

ShowMessage( Clientes[2] );

nos mostrará CARLOS ABENZA MARTINEZ.

En cualquier momento se puede modificar un elemento de la lista:

Clientes[3] := 'ROSA GUILLÉN LUNA';

Se puede recorrer cada elemento y mostrarlo en un campo Memo:

var
i: Integer;
begin
for i := 0 to Clientes.Count - 1 do
Memo.Lines.Add( Clientes[i] );
end;

Como muestra arriba se puede averiguar el número de elementos de la lista mediante la propiedad Count. Aunque hay una manera mucho más fácil de mostrar los elementos. Un objeto StringList dispone de la propiedad Text que nos devuelve todos los elementos de la lista separados en líneas. Por ejemplo:

ShowMessage( Clientes.Text ) nos devuelve:

PEDRO SANCHEZ GARCIA
MARIA PALAZÓN PÉREZ
CARLOS ABENZA MARTINEZ
ROSA GUILLÉN LUNA

También podemos obtener cada elemento separado por comas:

ShowMessage( Clientes.CommaText ) nos devuelve:

"PEDRO SANCHEZ GARCIA","MARIA PALAZÓN PÉREZ","CARLOS ABENZA MARTINEZ","ROSA GUILLÉN LUNA"

Nos ha devuelto el resultado separando cada elemento con comas además de encerrar cada uno con comillas dobles. Si nos interesa cambiar las comillas dobles por comillas simples, un StringList dispone de la propiedad QuoteChar la cual define el carácter delimitador de la cadena. Después solo hay que consultar el resultado mediante el valor DelimitedText en lugar de Text:

Clientes.QuoteChar := ''''; // o también Clientes.QuoteChar := #39;
ShowMessage( Clientes.DelimitedText );

El resultado sería:

'PEDRO SANCHEZ GARCIA','MARIA PALAZÓN PÉREZ','CARLOS ABENZA MARTINEZ','ROSA GUILLÉN LUNA'

Y por supuesto se puede cambiar el separador de los elementos de la lista (la coma):

Clientes.Delimiter := '-';
ShowMessage( Clientes.DelimitedText );

Lo cual mostraría:

'PEDRO SANCHEZ GARCIA'-'MARIA PALAZÓN PÉREZ'-'CARLOS ABENZA MARTINEZ'-'ROSA GUILLÉN LUNA'

ALMACENANDO VALORES DOBLES

Otra característica que hace extremadamente potente a los StringList es la posibilidad de almacenar valores dobles, es decir, cada elemento de la lista puede contener un nombre y un valor a la vez. Veamos un ejemplo:

var
Diccionario: TStringList;
begin
Diccionario := TStringList.Create;
Diccionario.CommaText := 'BOOK=LIBRO, MOUSE=RATON, TABLE=MESA';
Memo.Lines.Add( 'BOOK = ' + Diccionario.Values['BOOK'] );
Memo.Lines.Add( 'MOUSE = ' +Diccionario.Values['MOUSE'] );
Memo.Lines.Add( 'TABLE = ' +Diccionario.Values['TABLE'] );
Diccionario.Free;
end;

El resultado que nos mostraría dentro del campo Memo sería:

BOOK = LIBRO
MOUSE = RATON
TABLE = MESA

Se podría también utilizar para almacenar un registro de una base de datos en memoria antes de ejecutar la SQL:

var
Tabla: TStringList;
begin
Tabla := TStringList.Create;
Tabla.CommaText := 'ID=1, NOMBRE=CARLOS, DNI=65872841R, SALDO=130.25';
Memo.Lines.Add( 'ID = ' + Tabla.Values['ID'] );
Memo.Lines.Add( 'NOMBRE = ' + Tabla.Values['NOMBRE'] );
Memo.Lines.Add( 'DNI = ' +Tabla.Values['DNI'] );
Memo.Lines.Add( 'SALDO = ' + Tabla.Values['SALDO'] );
Tabla.Free;
end;

Su resultado sería:

ID = 1
NOMBRE = CARLOS
DNI = 65872841R
SALDO = 130.25

OPERACIONES QUE SE PUEDEN REALIZAR DENTRO DE UNA LISTA

Una de las operaciones más básicas de una lista es la ordenación mediante el comando Sort:

Clientes.Sort;
Memo.Lines.Add( Clientes.Text );

La lista quedaría de la siguiente manera:

CARLOS ABENZA MARTINEZ
MARIA PALAZÓN PÉREZ
PEDRO SANCHEZ GARCIA
ROSA GUILLÉN LUNA

Para añadir elementos hemos visto el comando Add:

Clientes.Add( 'LUIS GARCIA ROJO' );

También se puede utilizar el comando Append:

Clientes.Append( 'MANUEL ESCUDERO BERNAL' );

La diferencia esta en que Add devuelve la posición donde se ha insertado el elemento, en cambio Append no devuelve nada. La lista quedaría de la siguiente manera:

CARLOS ABENZA MARTINEZ
MARIA PALAZÓN PÉREZ
PEDRO SANCHEZ GARCIA
ROSA GUILLÉN LUNA
LUIS GARCIA ROJO
MANUEL ESCUDERO BERNAL

Para añadir elementos a la lista también disponemos del comando Insert el cual toma como primer parámetro en que posición de la lista deseamos insertar el elemento y como segundo parámetro el elemento en cuestión. Por ejemplo vamos a introducir un cliente entre CARLOS y MARIA:

Clientes.Insert( 1, 'PASCUAL CAMPOY FERNANDEZ' );

Y la lista quedaría así:

CARLOS ABENZA MARTINEZ
PASCUAL CAMPOY FERNANDEZ
MARIA PALAZÓN PÉREZ
PEDRO SANCHEZ GARCIA
ROSA GUILLÉN LUNA
LUIS GARCIA ROJO
MANUEL ESCUDERO BERNAL

En el próximo artículo seguiremos viendo más cosas que se pueden hacer con un StringList.

Pruebas realizadas en Delphi 7.

13 agosto 2007

Operaciones con cadenas de texto (y V)

Aquí finalizamos con las funciones de manipulación de cadenas de texto:

procedure SetLength( var S; NewLength: Integer );

Establece el tamaño de una cadena de texto o un array dinámico. Por ejemplo:

var
sTexto: String;
begin
sTexto := '123456789';
SetLength( sTexto, 4 );
end;

Después de ejecutar esto sTexto será igual a 1234 (hemos recortado la cadena).

También se utiliza para establecer el tamaño de un array dinámico antes de utilizarlo.

var
Valores: array of Integer;
begin
SetLength( Valores, 3 );
Valores[0] := 10;
Valores[1] := 20;
Valores[2] := 30;

// Ampliamos el tamaño del array
SetLength( Valores, 5 );
Valores[3] := 40;
Valores[4] := 50;
end;

procedure SetString( var S: string; Buffer: PChar; len: Integer );

El procedimiento SetString fija la longitud de la cadena S antes de copiar el número de caracteres determinado por len de la cadena Buffer. Vamos a ver un ejemplo que copia el contenido de un array de caracteres a un string:

var
Original: array[1..3] of char;
sDestino: String
begin
Original[1] := 'A';
Original[2] := 'B';
Original[3] := 'C';
SetString( sDestino, PChar( Addr( Original ) ), 3 );
end;

Ahora la variable sDestino vale 'ABC'.

function StringOfChar( Ch: Char; Count: Integer ): string;

Esta función devuelve una nueva cadena de texto creada con el carácter Ch y repetido tantas veces como determine el parámetro Count. Por ejemplo:

StringOfChar( '0', 5 ) devuelve 00000
StringOfChar( 'A', 6 ) devuelve AAAAAA

Esta función viene bien para completar con ceros por la derecha o por la izquierda los números que se pasan a formato texto.

function StringReplace( const S, OldPattern, NewPattern: string; Flags: TReplaceFlags ): string;

Esta función devuelve una cadena de texto reemplazando las subcadenas encontradas dentro de la cadena S que coincidan con OldPattern y serán sustituidas por la cadena NewPattern. Los valores del parámetro Flags pueden ser:

rfReplaceAll: reemplaza todas las cadenas encontradas
rfIgnoreCase: no distingue mayúsculas de minúsculas

Por ejemplo:

StringReplace( 'CAMBIANDO-EL-TEXTO', '-', '.', [] ) devuelve:

CAMBIANDO.EL-TEXTO

StringReplace( 'CAMBIANDO-EL-TEXTO', '-', '.', [rfReplaceAll] ) devuelve:

CAMBIANDO.EL.TEXTO

StringReplace( 'CAMBIANDO EL TEXTO', 'EL', 'mi', [rfReplaceAll,rfIgnoreCase] ) devuelve:

CAMBIANDO mi TEXTO

StringReplace( 'CAMBIANDO EL TEXTO', 'EL', 'mi', [rfReplaceAll] ) devuelve:

CAMBIANDO mi TEXTO

StringReplace( 'cambiando el texto', 'EL', 'mi', [rfReplaceAll] ) devuelve:

cambiando el texto

StringReplace( 'cambiando el texto', 'EL', 'mi', [rfReplaceAll,rfIgnoreCase] ) devuelve:

cambiando mi texto

function StrScan( const Str: PChar; Chr: Char ): PChar;

Devuelve un puntero al primer carácter que encuentre dentro de la cadena Str. Por ejemplo:

StrScan( 'BUSCANDO UN CARÁCTER', 'T' ) devuelve TER
StrScan( 'BUSCANDO UN CARÁCTER', 'U' ) devuelve USCANDO UN CARÁCTER

function StuffString( const AText: string; AStart, ALength: Cardinal; const ASubText: string ): string;

Esta función devuelve la cadena AText insertando dentro de la misma ASubText en la posición AStart y con la longitud ALength (aunque realmente no inserta sino que es una sustitución del texto que hay en esa misma posición). Veamos un ejemplo:

StuffString( 'INSERTANDO UN TEXTO EN UNA CADENA', 12, 2, 'EL' ) devuelve:

INSERTANDO EL TEXTO EN UNA CADENA

StuffString( 'INSERTANDO UN TEXTO EN UNA CADENA', 12, 15, 'UNA' ) devuelve:

INSERTANDO UNA CADENA

Si nos fijamos en el segundo ejemplo hemos sustituido UN TEXTO EN UNA por UNA (15 caracteres).

function Trim( const S: string ): string; overload;
function Trim( const S: WideString ): WideString; overload;


La función Trim devuelve la cadena S eliminando los espacios en blanco y los caracteres de control que halla a la izquierda y derecha (pero no en medio). Por ejemplo:

Trim( ' ESTO ES UNA PRUEBA ' ) devuelve 'ESTO ES UNA PRUEBA'

function TrimLeft( const S: string ): string; overload;
function TrimLeft( const S: WideString ): WideString; overload;


Esta función es similar a Trim salvo que sólo elimina los espacios en blanco y caracteres de control por la izquierda. Por ejemplo:

TrimLeft( ' ESTO ES UNA PRUEBA ' ) devuelve 'ESTO ES UNA PRUEBA '

function TrimRight( const S: string ): string; overload;
function TrimRight( const S: WideString ): WideString; overload;


Esta función es igual a Trim salvo que sólo elimina los espacios en blanco y caracteres de control por la derecha. Por ejemplo:

TrimRight( ' ESTO ES UNA PRUEBA ' ) devuelve ' ESTO ES UNA PRUEBA'

function UpCase( Ch: Char ): Char;

Convierte un carácter a mayúsculas. Por ejemplo:

UpCase( 'a' ) devuelve A
UpCase( 'ú' ) devuelve ú

Como vemos en el segundo ejemplo no funciona correctamente con caracteres Ansi. Mejor utilizar en su lugar la función AnsiUpperCase.

function UpperCase( const S: string ): string;

Convierte la cadena S a mayúsculas. Por ejemplo:

UpperCase( 'Hola' ) devuelve HOLA
UpperCase( 'Programación en Delphi' ) devuelve PROGRAMACIóN EN DELPHI

El segundo ejemplo no respeta las vocales con tilde. Mejor utilizar AnsiUpperCase.

function WrapText( const Line, BreakStr: string; nBreakChars: TSysCharSet; MaxCol: Integer ):string; overload;
function WrapText( const Line, MaxCol: Integer = 45 ):string; overload;


La función WrapText parte la cadena Line en múltiples líneas de texto separadas por defecto con los caracteres de control #13 y #10, en tamaños máximos de palabra definidos por MaxCol. Veamos algunos ejemplos:

WrapText( 'Partiendo una cadena de texto', 15 ) devuelve

Partiendo una
cadena de texto

WrapText( 'Partiendo una cadena de texto', 10 ) devuelve:

Partiendo
una
cadena de
texto

WrapText( 'Partiendo una cadena de texto', 5 ) devuelve:

Partiendo
una
cadena
de
texto

Es decir, parte la cadena en frases cuyo tamaño máximo es definido por MaxCol pero respetando en ancho de cada palabra (aunque se pase del límite). También podemos definir con que caracteres vamos a separar las palabras así como que deseamos de separador. Si quisieramos separar la frase mediante por punto sería de la siguiente manera:

WrapText( 'Partiendo.una.cadena.de.texto', '-', ['.'], 5 ) devuelve

Partiendo.-una.-cadena.-de.-texto

Hemos definido el separador (quitando #13 y #10) y también le hemos indicado el carácter que divide cada palabra (el punto).

Con esto finalizan las funciones de manipulación de texto.

Pruebas realizadas en Delphi 7.

Publicidad