29 agosto 2007

Trabajando con archivos de texto y binarios (II)

Vamos a ver otra manera de manejar ficheros de texto utilizando algunas clases que lleva Delphi para hacer nuestra labor mucho más fácil.

Para ello utilizaremos la clase TFileStream que hereda de la clase TStream para manejar flujos de datos, en este caso archivos. Una de las ventanas de utilizar FileStream en vez de los clásicos métodos de pascal tales como Rewrite, WriteLn, etc. es que controla automáticamente los buffer en disco según el tamaño de los mismos en Windows.

CREANDO UN ARCHIVO DE TEXTO USANDO LA CLASE TFILESTREAM

La clase TFileStream proporciona un método rápido y flexible para el manejo de archivos. Veamos como crear un archivo detexto:

procedure TFormulario.CrearArchivoStream;
var
F: TFileStream;
s: String;
begin
F := TFileStream.Create( ExtractFilePath( Application.ExeName ) + 'prueba.txt', fmCreate );
s := 'Añadiendo información al archivo de texto.' + #13 + #10;
F.Write( s[1], Length( s ) );
F.Free;
end;

El constructor Create de la clase TFileStream toma como primer parámetro la ruta del archivo y como segundo parámetro el tipo de acceso. Los tipos de acceso son:

fmCreate -> Crea un nuevo archivo. Si el archivo ya existe lo sobrescribe.
fmOpenRead -> Abre el archivo en modo de solo lectura.
fmOpenWrite -> Abre el archivo en modo de escritura.
fmOpenReadWrite -> Abre el archivo en modo lectura/escritura.

Después se graba la información mediante el método Write el cual lee el contenido de un buffer (puede ser texto o binario) pasando como segundo parámetro su longitud (Length). Finalmente liberamos la clase con Free y el mismo objeto cierra automáticamente el archivo.

Le hemos pasado como primer parámetro s[1] porque es la dirección de memoria donde comienza la variable s (los string empiezan por 1). Al final de la cadena le hemos metido cambién los caracteres de retorno de carro para que pase a la siguiente línea, ya que un objeto FileStream lo trata todo como texto continuo.

Cuando la cantidad de información a mover es muy grande, éste metodo es mucho más rápido que utilizar el clasico Rewrite, WriteLn, etc. (a menos claro que creemos nuestro buffer).


AÑADIENDO TEXTO A UN ARCHIVO CON FILESTREAM

Para añadir líneas a nuestro archivo creado anteriormente abrimos el archivo en modo fmOpenWrite, nos vamos al final del archivo utilizando la propiedad Position y añadimos el texto:

procedure TFPrincipal.AnadirArchivoStream;
var
F: TFileStream;
s: String;
begin
F := TFileStream.Create( ExtractFilePath( Application.ExeName ) + 'prueba.txt', fmOpenWrite );
F.Position := F.Size;
s := 'Añadiendo información al archivo de texto.' + #13 + #10;
F.Write( s[1], Length( s ) );
F.Free;
end;

Si no se hubiese utilizado la propiedad Position habría machadado la información introducida anteriormente. Podemos movernos a nuestro antojo con la propiedad Position para guardar información en un fichero en cualquier sitio. Para irse al principio de un archivo sólo hay que hacer:

F.Position := 0;

LEYENDO LOS DATOS MEDIANTE FILESTREAM

El siguiente procedimiento lee el contenido del archivo en modo fmOpenRead en la variable s que hace de buffer y posteriormente lo manda al memo:

procedure TFPrincipal.CargarArchivoStream;
var
F: TFileStream;
s: String;
begin
F := TFileStream.Create( ExtractFilePath( Application.ExeName ) + 'prueba.txt', fmOpenRead );
SetLength( s, F.Size ); // *** Me faltaba esto, gracias por la corrección ***
F.Read( s[1], F.Size );
Memo.Text := s;
F.Free;
end;

Aunque en este caso no tenemos la flexibilidad que nos aportaban las funciones ReadLn y WriteLn para archivos de texto.

BLOQUEANDO EL ARCHIVO A OTROS USUARIOS

Una de las cualidades de las que goza el objeto FileStream es la de bloquear el acceso a otros usuarios mientras estamos trabajando con un archivo. La protección se realiza cuando se abre el archivo. Por ejemplo, si queremos abrir el archivo en modo lectura exclusivamente para nosotros hacemos:

F := TFileStream.Create( ExtractFilePath( Application.ExeName ) + 'prueba.txt', fmReadOnly or fmShareExclusive );

Mediante el operador OR mezclamos las opciones de apertura con las opciones de bloqueo. Las posibles opciones de bloqueo son:

fmShareCompat -> Permite compatibilizar la apertura con otro usuario o programa
fmShareExclusive -> Sólo nosotros tenemos derecho acceso de lectura/escritura mientras este abierto
fmShareDenyWrite -> Bloqueamos el archivo para que nadie pueda escribir salvo nosotros
fmShareDenyRead -> Bloqueamos el archivo para que nadie pueda leerlo salvo nosotros
fmShareDenyNone -> Permite la lectura/escritura por parte de otros usuarios.

También se podrían manipular archivos de texto facilmente mediante objetos TStringList como vimos con anterioridad. Yo personalmente utilizo TStringList, objetos Memo o RitchEdit ya que permiten una manipulación del texto en memoria bastante flexible antes de guardarla en disco.

En el próximo artículo veremos el tratamiento de archivos binarios.

Pruebas realizadas en Delphi 7.

5 comentarios:

Anónimo dijo...

En
LEYENDO LOS DATOS MEDIANTE FILESTREAM falta una instruccion. Aqui es lo como deberia ser:
procedure TFPrincipal.CargarArchivoStream;
var F: TFileStream;
s: String;
begin
F := TFileStream.Create( ExtractFilePath( Application.ExeName ) + 'prueba.txt', fmOpenRead );
SetLength(s,F.Size);
F.Read( s[1], F.Size );
Memo1.Text := s;
F.Free;

Anónimo dijo...

Efectivamente si no se le da el tamaño a 's' antes de leer se produce un desbordamiento de memoria.

Es correcto, falta:

SetLength(s,F.Size);

Anónimo dijo...

si voy al medio del archivo y escribo cualquier cosa, eso sobreescribe el contenido en la posición actual, no? si yo quiero que en la mitad del archivo se 'agregue' mi información y que a partir de ahi continue la información actual, ¿tendría que hacerlo a mano? ¿como pasar toda la segunda mitad de información a memoria y después reescribirla?

gracias!

Administrador dijo...

Te recomiendo generar un nuevo archivo binario o de texto (según sea tu caso) con la información modificada.

Deja el original como está.

Cristina dijo...

Como hago para utilizar el nombre del archivo .txt y compararlo con una variable tipo string?

Publicidad