lunes, 5 de noviembre de 2012

Manejo de archivos


Manejo de archivos en Objective-C

Uno de los temas mas importantes para hacer aplicaciones robustas con Objective-C es el manejo de archivos. En el blog anterior se vió cómo manejar los directorios con la clase NSFileManager y las clases NSFileHandle and NSData dentro del Framework-Foundation.
Ahora veremos el apasionante tema de crear, manejar y eliminar datos.
 

El directorio por referencia

  1. Primero debemos de obtener el directorio de la aplicación, la cual por lo general la conocemos como "home", "root" o "raiz". 
  2. Esta referencia raìz la obtenemos con el método defaultManager de la clase NSFileManager, creando una instancia de la misma:

NSFileManager *raiz;
raiz= [NSFileManager defaultManager];

Verificar si existe un archivo

  1. La clase NSFileManager contiene un método llamado fileExistsAtPath el cual verifica la existencia de un archivo. 
  2. El método toma como argumento una cadena NSString y regresará un valor booleano verdadero (YES) si el archivo existe o un valor negativo (NO) de lo contrario:

NSFileManager *raiz;

raiz = [NSFileManager defaultManager];

if ([raiz fileExistsAtPath: @"/tmp/myfile.txt" ] == YES)
        NSLog (@"Si existe el archivo");
else
        NSLog (@"Archivo no encontrado");

Comparación de dos archivos

Podemos comparar el contenido de dos archivos por medio del método contentsEqualAtPath. Este método toma como argumentos al nombre y el directorio de los dos archivos. Regresaa un valor falso (NO) si es que son diferentes y un verdadero (YES) si los contenidos son iguales:

NSFileManager *raiz;

raiz= [NSFileManager defaultManager];

if ([raiz contentsEqualAtPath: @"/tmp/compras.txt" andPath: @"/tmp/ventas.txt"] == YES)
        NSLog (@"Los contenidos son iguales");
else
        NSLog (@"Los contenidos son diferentes");

Verificando los permisos del archivo

La mayorìa de los sistemas operativos (de servidores, locales o de dispositivos móviles) asignan "permisos" a cada archivo y directorio. Por medio de los siguientes métodos podemos saber si se cuenta con el permiso respectivo: isReadableFileAtPath, isWritableFileAtPath, isExecutableFileAtPath y isDeletableFileAtPath. Cada uno regresa un valor booleano verdadero (YES) si el archivo tiene ese permiso o un falso (NO) si no lo tiene

NSFileManager *raiz;

raiz = [NSFileManager defaultManager];

if ([raiz isWritableFileAtPath: @"/tmp/myfile.txt"]  == YES)
        NSLog (@"El archivo tiene permiso para ser escrito");
else
        NSLog (@"El archivo es sólo lectura");

Mover o renombrar un archivo

 Un archivo puede ser renombrado, en caso que tenga ese permiso, usando el método moveItemAtURL. Este método regresará un valor booleano verdadero (YES) si la acción de renombrar fue exitosa y uno falso (NO) de lo contrario. Se tiene un objeto de tipo error NSError, el cual devuelve un valor nil si la operación fue exitosa, y un objeto de error si la operación de renombrar falló:

NSFileManager *raiz;

raiz = [NSFileManager defaultManager];

NSURL *dirAct = [NSURL fileURLWithPath:@"/tmp/archivoActual.txt"];
NSURL *dirNuevo= [NSURL fileURLWithPath:@"/tmp/archivoNuevo.txt"];

[raiz moveItemAtURL: dirAct toURL: dirNuevo error: nil];

Copiar un archivo

Por medio del método copyItemAtPath es posible copiar un archivo. Los argumentos y valores de respuesta son los mismos que en el proceso de renombrar el archivo que se vieron en el segmento anterior.

NSFileManager *raiz;

raiz= [NSFileManager defaultManager];

if ([raiz copyItemAtPath: @"/tmp/archivoActual.txt" toPath: @"/Users/demo/archivoNuevo.txt" error: NULL]  == YES)
        NSLog (@"Copia exitosa");
else
        NSLog (@"Error en la copia");


Borrar un archivo

El método removeItemAtPath permite borrar o remover un archivo. El método toma el ombre de archivo con su camino (opcional) y un objeto de la clase NSError. Regresa un valor booleano en respuesta a la operación, indicando si la misma fue exitosa (YES) o no (NO).

NSFileManager *raiz;

raiz = [NSFileManager defaultManager];

if ([raiz removeItemAtPath: @"/tmp/miArchivo.txt" error: NULL]  == YES)
        NSLog (@"Borrado exitoso");
else
        NSLog (@"Error al borrar");

Crear un link simbólico

Podemos crear un link simbólico a un archivo por medio del método createSymbolicLinkAtPath. Los argumentos de este mètodo son el camino que deseamos volver link simbòlico, el nombre del archivo y un objeto de la clse NSError, que regresa algùn valor si la operaciòn no fue satisfactoria.

NSFileManager *raiz;

raiz = [NSFileManager defaultManager];

if ([raiz createSymbolicLinkAtPath: @"/tmp/archivo2.txt"
                withDestinationPath: @"/tmp/archivo.txt" error: nil] == YES)
        NSLog (@"Ligado exitoso");
else
        NSLog (@"Error al crear el link");

Leer y escribir archivos con NSFileManager

Una forma muy básica y limitada de leer y escribir archivos es por medio de la clase NSFileManager.
El contenido que se va a grabar debe de estar contenido en un objeto de tipo NSData antes de utilizar el método contentsAtPath:

NSFileManager *raiz;
NSData *databuffer;

raiz = [NSFileManager defaultManager];

databuffer = [raiz contentsAtPath: @"/tmp/archivo.txt" ];

Una vez que los datos están almacenados en el objeto NSData, podemos utilizar el método createFileAtPath para escribir el archivo:

databuffer = [raiz contentsAtPath: @"/tmp/archivo.txt" ];

[raiz createFileAtPath: @"/tmp/ArchivoNuevo.txt" contents: databuffer attributes: nil];




Esta forma de crear un archivo no nos permite saber cuánta informaciçon se copió ni hacer una operación de append al final del mimso.i el archivo destino ya exste, se sobreescribe perdiendo los datos anteriores. Una forma mucho màs robusta de grabar información es por medio de la clase NSFileHandle perteneciente al Framework-Fundation.

Uso de la clase NSFileHandle para la operación con archivos

The NSFileHandle class provides a range of methods designed to provide a more advanced mechanism for working with files. In addition to files, this class can also be used for working with devices and network sockets. In the following sections we will look at some of the more common uses for this class.

Crear un objeto NSFileHandle

Un objeto NSFileHandle al abrir un archivo para leer, escribir o añadir datos al final del mismo. Para ello se pueden utilizar, respectivamente, los siguientes métodos:
  • fileHandleForReadingAtPath
  • fileHandleForWritingAtPath
  •  fileHandleForUpdatingAtPath 

Una vez efectuado su operación, se deberà cerrar con el método closeFile. Si el proceso de apertura del archivo falla, el mètodo regresará un valor de nil.
NSFileHandle *archivo;

archivo = [NSFileHandle fileHandleForWritingAtPath: @"/tmp/archivo.txt"];

if (archivo == nil)
        NSLog(@"Error al abrir el archivo");

[archivo closeFile];

El proceso de desplazamiento (Offsets) y búsqueda (Seeking)

  1. Un objeto de la clase NSFileHandle mantiene un apuntador para marcar la posición actual del archivo. 
  2. Esta posición se le conoce como offset, o el desplazamiento del apuntador con respecto al inicio del archivo. 
  3. Cuando el archivo es abierto, este apuntador se encuentra al inicio del mismo y su valor es cero (0).
  4. Esto quiere decir que cualquier operacon iniciará cvon un valor del apuntador de cero.
  5. Para efectuar una operación en otra parte del archivo, digamos al final, necesitamos primero buscar (seek) ese desplazamiento (offset).
  6. Por ejemplo, si desea mover el apuntador al final del archivo, puede hacerlo por medio del método  seekToEndOfFile
  7. Si desea mover el apuntador a un lugar en específico, puede utilizar el método seekToFileOffset el cual le permite especificar el lugar exacto dentro del archivo. 
  8. Para identificar el lugar donde se encuentra el apuntador, puede hacerlo con el método offsetInFile
  9. Para que el apundador puede almacenar su ubicación en un archivo muy grande, su tipo es un entero  unsigned long long.
Por ejemplo:

NSFileHandle * archivo;

archivo = [NSFileHandle fileHandleForUpdatingAtPath: @"/tmp/archivo.txt"];

if (archivo == nil)
        NSLog(@"Error al abrir el archivo");

NSLog (@"Offset = %llu", [archivo offsetInFile]);

[archivo seekToEndOfFile];

NSLog (@"Offset = %llu", [archivo offsetInFile]);

[archivo seekToFileOffset: 30];

NSLog (@"Offset = %llu", [archivo offsetInFile]);

[archivo closeFile];

 Leer datos en un archivo

  1. Una vez que se ha abierto el archivo y que tenemos la referencia, el contenido del mismo puede ser leido a partir de la posición del apuntador (offset).
  2. El método readDataOfLength lee un número especificado de números de bytes a partir del apuntador (offset). 
  3. Por ejemplo, el siguiente código lee 5 bytes a partir de la posiciçon 10 a partir del inicio del archivo. 
  4. La información leída es encapsulada dentro de un objeto NSData:

NSFileHandle * archivo;
NSData *databuffer;

archivo = [NSFileHandle fileHandleForReadingAtPath: @"/tmp/archivo.txt"];

if (archivo == nil)
        NSLog(@"Failed to open file");

[archivo seekToFileOffset: 10];

databuffer = [archivo readDataOfLength: 5];

[archivo closeFile];

El método readDataToEndOfFile lee los datos encontrados desde el offset hasta el fin del archivo.

Escribir los datos en el archivo

El método writeData escribe la información encontrada en un objeto NSData a partir de la ubicación del apuntador u offset.
Este proceso no inserta datos, más bien los sobre escribe.
Para ver este comando en acción, cree un archivo de texto con la sentencia:

hola, cara de bola

A continuación ejecute el siguiente código:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
    @autoreleasepool {

        NSFileHandle * archivo;
        NSMutableData *data;

        const char *bytestring = "black dog";

        data = [NSMutableData dataWithBytes:bytestring length:strlen(bytestring)];


        archivo = [NSFileHandle fileHandleForUpdatingAtPath: @"/tmp/archivo.txt"];

        if (archivo == nil)
                NSLog(@"Error al abrir el archivo");


        [archivo seekToFileOffset: 10];

        [archivo writeData: data];

        [archivo closeFile];

    }
    return 0;
}

Abra con un editor el archivo para ver el cambio.

Truncar un archivo

Un archivo puede ser truncado por medio del método truncateFileAtOffset.
Para borrar todo el contenido del archivo, el apuntador u offset deberá encontrarse al inicio del archivo, es decir, ser cero:

        NSFileHandle * archivo;

        archivo = [NSFileHandle fileHandleForUpdatingAtPath: @"/tmp/archivo.txt"];

        if (archivo == nil)
                NSLog(@"Error al abrir el archivo");

        [archivo truncateFileAtOffset: 0];

        [archivo closeFile];
 

No hay comentarios:

Publicar un comentario