|
Hace un tiempo los programas Windows usaban archivos
de configuración (*.INI) para almacenar información relacionada
con las aplicaciones. Sin embargo, con la aparición de los Windows
de 32 bits (Win 95 en adelante), se decidió que la nueva forma de
hacer las cosas era usando el Registro. Allí se debía guardar toda
la información relacionada con la configuración de las aplicaciones
y del sistema operativo. Es así que el "saber popular"
de hoy en día nos dice que el uso de los INI está fuera de moda
y que lo que hay que hacer es usar el Registro.
A pesar de ello, los archivos INI tienen muchas ventajas
que el Registro no tiene. En primer lugar, al borrar un programa,
también estaremos borrando todos los archivos INI que guardaban
las configuraciones de ese programa, a diferencia de lo que generalmente
ocurre cuando estas configuraciones se guardan en el Registro. No
es raro oir que programas que habíamos borrado hace un tiempo siguen
teniendo claves aquí y allá, y algunas más que nunca descubriremos
donde están. En segundo lugar, los archivos INI, al ser archivos
de texto son mucho más fáciles de modificar. Y por último, la frase
"divide y triunfarás" ¿la conoces?. El Registro se ha
convertido en una base demasiado grande y cuanto más grande más
problemas.
Como notarás, yo sigo utilizando los archivos INI
para almacenar todas las configuraciones de mis programas. Sin embargo,
mientras esté programando bajo Windows nunca podré librarme de utilizar
el Registro, ya que en él se guardan todas las configuraciones del
sistema y de muchos de los programas que se encuentran instalados
en él.
Muy bien, pero todavía no me has dicho qué son los
archivos INI. Bueno, los archivos INI son simples archivos de texto
que se han popularizado por almacenar configuraciones de los programas
y del sistema. El archivo INI más popular es el WIN.INI, que Windows
todavía hoy utiliza para almacenar configuraciones del sistema operativo.
|
|
Para no tener que manejar estos archivos "a
la interperie", Delphi, al igual que con el Registro, nos provee
una clase para poder modificar los archivos INI de una forma muy
sencilla. Antes de ver cómo funciona TIniFile, veamos cuales son
sus propiedades y métodos más importantes.
|
Propiedad
|
Descripción
|
|
FileName
|
Almacena el nombre del archivo INI en el cuál
se podrán realizar las operaciones de lectura/escritura.
|
|
Método
|
Descripción
|
|
DeleteKey
|
Borra los datos asignados a la clave especificada.
|
|
EraseSection
|
Borra toda la sección especificada, incluyendo
todas las claves y valores que se encontraban en ella.
|
|
ReadSection
|
Devuelve los nombres de todas las claves de
la sección especificada.
|
|
ReadSections
|
Devuelve los nombres de todas las secciones
existentes.
|
|
ReadSectionValues
|
Devuelve los valores pertenecientes a todas
las claves de la sección especificada.
|
|
ReadString, ReadBool, ReadDate, ReadTime, ReadDateTime,
ReadFloat, ReadInteger.
|
Todos estos métodos realizan la misma operación:
leer datos desde el archivo INI. La única diferencia es que
cada uno lee diferentes tipos de datos.
|
|
WriteString, WriteBool, WriteDate, WriteTime,
WriteDateTime, WriteFloat, WriteInteger.
|
Todos estos métodos realizan la misma operación:
escribe datos desde el archivo INI. La única diferencia es
que cada uno escribe diferentes tipos de datos.
|
Bueno, ahora si, llegó la hora de ver a TIniFile
en acción. Veamos cómo hacer el mismo programa que guardaba configuraciones
(tamaño, posición, etc.) en el Registro, pero esta vez guardándolas
en un archivo INI.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
IniFiles, StdCtrls, ToolWin, ComCtrls, Menus;
type
TForm1 = class(TForm)
btnAbrir: TButton;
ToolBar1: TToolBar;
MainMenu1: TMainMenu;
mnuOpcionesToolbar1: TMenuItem;
RichEdit1: TRichEdit;
OpenDialog: TOpenDialog;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnAbrirClick(Sender: TObject);
private
UltimoAbierto: string;
procedure AbrirArchivo(Ruta: string);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
const RutaINI: string = 'c:\windows\pruebaini.ini';
procedure TForm1.AbrirArchivo(Ruta: string);
begin
{ abrimos el archivo y realizamos algunas modificaciones a la barra de título }
try
if Ruta <> '' then begin
RichEdit1.Lines.LoadFromFile(Ruta);
Caption := 'Notas - ' + Ruta;
UltimoAbierto := Ruta;
end;
except
on EFOpenError do
ShowMessage('El archivo que se abrió la última vez ya no existe!');
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var Ini: TIniFile;
SeccionExiste: boolean;
begin
Ini := TIniFile.Create(RutaINI);
try
SeccionExiste := Ini.SectionExists('Tamaño');
{ obtenemos todos los datos necesarios del Registro }
if SeccionExiste then begin
if Ini.ReadBool('Tamaño', 'Maximizado', False) = False then begin
Left := Ini.ReadInteger('Tamaño', 'Left', (Screen.Width div 2) -
(Width div 2));
Top := Ini.ReadInteger('Tamaño', 'Top', (Screen.Height div 2) -
(Height div 2));
Height := Ini.ReadInteger('Tamaño', 'Height', 480);
Width := Ini.ReadInteger('Tamaño', 'Width', 696);
end else
WindowState := wsMaximized;
ToolBar1.Visible := Ini.ReadBool('Otras', 'ToolBar1', True);
mnuOpcionesToolBar1.Checked := ToolBar1.Visible;
AbrirArchivo(Ini.ReadString('Otras', 'UltimoAbierto', ''));
end;
finally
Ini.Free;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var Ini: TIniFile;
begin
Ini := TIniFile.Create(RutaINI);
try
{ guardamos todos los datos en el archivo INI }
Ini.WriteBool('Tamaño', 'Maximizado', WindowState = wsMaximized);
Ini.WriteInteger('Tamaño', 'Left', Left);
Ini.WriteInteger('Tamaño', 'Top', Top);
Ini.WriteInteger('Tamaño', 'Height', Height);
Ini.WriteInteger('Tamaño', 'Width', Width);
Ini.WriteBool('Otras', 'ToolBar1', mnuOpcionesToolBar1.Checked);
Ini.WriteString('Otras', 'UltimoAbierto', UltimoAbierto);
finally
Ini.Free;
end;
end;
procedure TForm1.btnAbrirClick(Sender: TObject);
begin
if OpenDialog.Execute then
AbrirArchivo(OpenDialog.FileName);
end;
end.
|
Como verás la traducción de una forma a la otra es
muy sencilla y todo lo que tuvimos que hacer es cambiar un par de
líneas. Son 2 formas de hacer exactamente lo mismo. Ambos métodos
tienen sus ventajas y desventajas que debes tener en cuenta a la
hora de elegir. Si me pides mi opinión, definitivamente prefiero
utilizar los archivos INI. Sin embargo, mis gustos no tienen por
qué ser los tuyos. Te recomendaría que probaras con ambos y decidas
cuál te resulta más cómodo.
En cuanto al ejemplo anterior hay algunos puntos
que sería interesante aclarar. Seguramente al leer el código te
habrás hecho alguna de estas preguntas:
a) ¿Qué pasa si llamamos al constructor de TIniFile
con un nombre de archivo inexistente como parámetro?
Rta: Si lo que hacemos luego de crear el objeto es intentar leer
el archivo, no pasa nada; en cambio si lo que hacemos es intentar
escribir el archivo se crea automáticamente.
b) ¿Qué pasa si intentamos escribir un valor en una
sección inexistente, y por lo tanto en una clave inexistente?
Rta: Automáticamente se crean la sección y la clave. En el caso
de que la sección ya exista pero la clave no, se crea sólo la clave,
por supuesto.
c) ¿Qué pasa si la sección/clave que intentamos leer
no existe o no hay valores asignados a esa clave?
Rta: Simplemente ReadLoQueSea devuelve el parámetro Default.
Eso es todo lo relacionado con los archivos INI.
Sin embargo, no quisiera terminar este artículo sin antes mencionar
que, aunque no las hemos utilizado en el ejemplo anterior, la clase
TIniFile viene también con 2 métodos para leer secciones enteras
de una vez. Esto resulta útil cuando se desconocen los nombres de
las claves de una sección en particular. Estos métodos están definidos
de la siguiente manera:
procedure ReadSection (const Section: string; Strings:
TStrings);
procedure ReadSectionValues(const Section: string; Strings: TStrings);
Pero como digo siempre, pocas palabras y más ejemplos.
Veamos a estos métodos en acción para comprender cuál es su uso
más común.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, IniFiles;
type
TForm1 = class(TForm)
lbSection: TListBox;
lbSectionValues: TListBox;
lbSections: TListBox;
procedure FormCreate(Sender: TObject);
procedure lbSectionsClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
WinIni : TIniFile;
begin
WinIni := TIniFile.Create('WIN.INI');
try
WinIni.ReadSections(lbSections.Items);
finally
WinIni.Free;
end;
end;
procedure TForm1.lbSectionsClick(Sender: TObject);
var
WinIni : TIniFile;
begin
lbSection.Clear;
lbSectionValues.Clear;
WinIni := TIniFile.Create('WIN.INI');
try
WinIni.ReadSection(lbSections.Items.
Strings[lbSections.ItemIndex], lbSection.Items);
WinIni.ReadSectionValues(lbSections.Items.
Strings[lbSections.ItemIndex], lbSectionValues.Items);
finally
WinIni.Free;
end;
end;
|
Nota: Si eres
un buen observador habrás notado que al crear el objeto WinIni el
parámetro es 'WIN.INI' y no 'C:\Windows\WIN.INI'. Esto se debe a
que en general los archivos INI se encuentran en el directorio Windows
y por lo tanto con sólo especificar el nombre del archivo basta.
Si deseas trabajar con un archivo localizado en otro directorio
debes especificar la ruta completa.
La diferencia entre ReadSection y ReadSectionValues
es que ReadSection devuelve sólo el nombre de todas las claves ("Wallpaper")
pertenecientes a la sección especificada, mientras que ReadSectionValues
devuelve no sólo los nombres sino también los valores de esas claves.
Por ejemplo, mientras ReadSection devuelve los siguientes valores
al buscar en la sección Desktop del WIN.INI:
Wallpaper
TileWallpaper
WallpaperStyle
Pattern
ReadSectionValues devuelve:
Wallpaper=C:\WINDOWS\PLUS!.BMP
TileWallpaper=0
WallpaperStyle=0
Pattern=(None)
Bueno, ahora sí hemos concluido. Pero arriba esos
ánimos, con cada final hay un nuevo comienzo. Revisa y no te quedes
con ninguna duda. Los espero la próxima, tratando vaya uno a saber
qué tema "complicado". ¡Suerte!
|