Además de las aplicaciones más simples, la mayoría de los programas tienen que leer o escribir archivos. Puede ser solo para leer un archivo de configuración, un analizador de texto o algo más sofisticado. Este tutorial se enfoca en usar archivos de acceso aleatorio en C.
Programación de E / S de archivos de acceso aleatorio en C
Las operaciones básicas de archivo son:
- fopen - abre un archivo - especifica cómo se abre (lectura / escritura) y escribe (binario / texto)
- fclose - cierra un archivo abierto
- fread - lee de un archivo
- fwrite: escribe en un archivo
- fseek / fsetpos: mueve un puntero de archivo a algún lugar de un archivo
- ftell / fgetpos: le indica dónde se encuentra el puntero del archivo
Los dos tipos de archivos fundamentales son texto y binario. De estos dos, los archivos binarios suelen ser más fáciles de manejar. Por esa razón y el hecho de que el acceso aleatorio en un archivo de texto no es algo que deba hacer con frecuencia, este tutorial se limita a los archivos binarios. Las primeras cuatro operaciones enumeradas anteriormente son para archivos de texto y de acceso aleatorio. Los dos últimos solo para acceso aleatorio.
El acceso aleatorio significa que puede moverse a cualquier parte de un archivo y leer o escribir datos desde él sin tener que leer todo el archivo. Hace años, los datos se almacenaban en grandes rollos de cinta de computadora. La única forma de llegar a un punto en la cinta era leyendo toda la cinta. Luego aparecieron los discos y ahora puede leer cualquier parte de un archivo directamente.
Programación con archivos binarios
Un archivo binario es un archivo de cualquier longitud que contiene bytes con valores en el rango de 0 a 255. Estos bytes no tienen otro significado a diferencia de un archivo de texto donde un valor de 13 significa retorno de carro, 10 significa avance de línea y 26 significa fin de archivo. El software que lee archivos de texto tiene que tratar con estos otros significados.
Los archivos binarios son una secuencia de bytes, y los lenguajes modernos tienden a funcionar con secuencias en lugar de archivos. La parte importante es el flujo de datos en lugar de su origen. En C, puede pensar en los datos como archivos o secuencias. Con acceso aleatorio, puede leer o escribir en cualquier parte del archivo o secuencia. Con acceso secuencial, debe recorrer el archivo o transmitir desde el principio como una gran cinta.
Este ejemplo de código muestra un archivo binario simple que se abre para escritura, con una cadena de texto (char *) que se escribe en él. Normalmente ves esto con un archivo de texto, pero puedes escribir texto en un archivo binario.
Este ejemplo abre un archivo binario para escribir y luego escribe un char * (cadena) en él. La variable FILE * se devuelve desde la llamada fopen (). Si esto falla (el archivo puede existir y estar abierto o de solo lectura o podría haber un error con el nombre del archivo), entonces devuelve 0.
El comando fopen () intenta abrir el archivo especificado. En este caso, es test.txt en la misma carpeta que la aplicación. Si el archivo incluye una ruta, todas las barras invertidas deben duplicarse. "c: \ folder \ test.txt" es incorrecto; debes usar "c: \\ folder \\ test.txt".
Como el modo de archivo es "wb", este código está escribiendo en un archivo binario. El archivo se crea si no existe, y si lo hace, se elimina lo que haya en él. Si falla la llamada a fopen, tal vez porque el archivo estaba abierto o el nombre contiene caracteres no válidos o una ruta no válida, fopen devuelve el valor 0.
Aunque podría verificar si ft no es cero (éxito), este ejemplo tiene una función FileSuccess () para hacer esto explícitamente. En Windows, muestra el éxito / error de la llamada y el nombre del archivo. Es un poco oneroso si busca rendimiento, por lo que puede limitar esto a la depuración. En Windows, hay poca sobrecarga de texto de salida al depurador del sistema.
Las llamadas fwrite () generan el texto especificado. El segundo y tercer parámetro son el tamaño de los caracteres y la longitud de la cadena. Ambos se definen como size_t, que es un entero sin signo. El resultado de esta llamada es escribir elementos de conteo del tamaño especificado. Tenga en cuenta que con los archivos binarios, aunque esté escribiendo una cadena (char *), no agrega ningún carácter de retorno de carro o avance de línea. Si desea esos, debe incluirlos explícitamente en la cadena.
Modos de archivo para leer y escribir archivos
Cuando abre un archivo, especifica cómo se abrirá: si crearlo a partir de nuevo o sobrescribirlo y si es texto o binario, leer o escribir y si desea agregarlo. Esto se hace usando uno o más especificadores de modo de archivo que son letras simples "r", "b", "w", "a" y "+" en combinación con las otras letras.
- r: abre el archivo para leer. Esto falla si el archivo no existe o no se puede encontrar.
- w: abre el archivo como un archivo vacío para escribir. Si el archivo existe, su contenido se destruye.
- a - Abre el archivo para escribir al final del archivo (anexando) sin quitar el marcador EOF antes de escribir nuevos datos en el archivo; esto crea el archivo primero si no existe.
Agregar "+" al modo de archivo crea tres modos nuevos:
- r +: abre el archivo para leer y escribir. (El archivo debe existir).
- w +: abre el archivo como un archivo vacío para leer y escribir. Si el archivo existe, su contenido se destruye.
- a + - Abre el archivo para leer y agregar; la operación de adición incluye la eliminación del marcador EOF antes de que se escriban nuevos datos en el archivo, y el marcador EOF se restaura después de que se completa la escritura. Primero crea el archivo si no existe. Abre el archivo para leer y agregar; la operación de adición incluye la eliminación del marcador EOF antes de que se escriban nuevos datos en el archivo, y el marcador EOF se restaura después de que se completa la escritura. Primero crea el archivo si no existe.
Combinaciones de modo de archivo
Esta tabla muestra combinaciones de modo de archivo para archivos de texto y binarios. En general, puede leer o escribir en un archivo de texto, pero no ambos al mismo tiempo. Con un archivo binario, puede leer y escribir en el mismo archivo. La siguiente tabla muestra lo que puede hacer con cada combinación.
- r texto - leer
- rb + binario - leer
- r + texto - leer, escribir
- r + b binario - leer, escribir
- rb + binario - leer, escribir
- w texto - escribir, crear, truncar
- wb binary - escribe, crea, trunca
- w + texto: leer, escribir, crear, truncar
- binario w + b - leer, escribir, crear, truncar
- wb + binario: leer, escribir, crear, truncar
- un texto - escribir, crear
- binario ab - escribe, crea
- a + texto - leer, escribir, crear
- binario a + b - escribir, crear
- ab + binario - escribir, crear
A menos que solo esté creando un archivo (use "wb") o solo esté leyendo uno (use "rb"), puede salirse con la suya usando "w + b".
Algunas implementaciones también permiten otras letras. Microsoft, por ejemplo, permite:
- t - modo de texto
- c - cometer
- n - sin compromiso
- S: optimización del almacenamiento en caché para acceso secuencial
- R: almacenamiento en caché no secuencial (acceso aleatorio)
- T - temporal
- D - eliminar / temporal, que mata el archivo cuando está cerrado.
Estos no son portátiles, así que úselos bajo su propio riesgo.
Ejemplo de almacenamiento de archivos de acceso aleatorio
La razón principal para usar archivos binarios es la flexibilidad que le permite leer o escribir en cualquier parte del archivo. Los archivos de texto solo le permiten leer o escribir secuencialmente. Con la prevalencia de bases de datos baratas o gratuitas como SQLite y MySQL, reduce la necesidad de usar acceso aleatorio en archivos binarios. Sin embargo, el acceso aleatorio a los registros de archivos es un poco anticuado pero sigue siendo útil.
Examinando un ejemplo
Suponga que el ejemplo muestra un par de archivos de índice y datos que almacenan cadenas en un archivo de acceso aleatorio. Las cadenas son de diferentes longitudes y están indexadas por posición 0, 1, etc.
Hay dos funciones nulas: CreateFiles () y ShowRecord (int recnum). CreateFiles utiliza un búfer char * de tamaño 1100 para contener una cadena temporal compuesta por el mensaje de cadena de formato seguido de n asteriscos donde n varía de 5 a 1004. Se crean dos ARCHIVOS * ambos usando wb filemode en las variables ftindex y ftdata. Después de la creación, se utilizan para manipular los archivos. Los dos archivos son
- index.dat
- data.dat
El archivo de índice contiene 1000 registros de tipo indextype; Este es el tipo de estructura, que tiene los dos miembros pos (de tipo fpos_t) y tamaño. La primera parte del bucle:
rellena el mensaje de cadena como este.
y así. Luego esto:
rellena la estructura con la longitud de la cadena y el punto en el archivo de datos donde se escribirá la cadena.
En este punto, tanto la estructura del archivo de índice como la cadena del archivo de datos pueden escribirse en sus respectivos archivos. Aunque estos son archivos binarios, se escriben secuencialmente. En teoría, podría escribir registros en una posición más allá del final actual del archivo, pero no es una buena técnica para usar y probablemente no sea portátil.
La parte final es cerrar ambos archivos. Esto asegura que la última parte del archivo se escriba en el disco. Durante las escrituras de archivo, muchas de las escrituras no van directamente al disco, sino que se almacenan en búferes de tamaño fijo. Después de que una escritura llena el búfer, todo el contenido del búfer se escribe en el disco.
Una función de descarga de archivos fuerza la descarga y también puede especificar estrategias de descarga de archivos, pero están destinadas a archivos de texto.
Función ShowRecord
Para probar que cualquier registro especificado del archivo de datos se puede recuperar, necesita saber dos cosas: dónde comienza en el archivo de datos y qué tan grande es.
Esto es lo que hace el archivo de índice. La función ShowRecord abre ambos archivos, busca el punto apropiado (recnum * sizeof (indextype) y obtiene una cantidad de bytes = sizeof (index).
SEEK_SET es una constante que especifica de dónde se realiza el fseek. Hay otras dos constantes definidas para esto.
- SEEK_CUR: busca en relación con la posición actual
- SEEK_END: busca el absoluto desde el final del archivo
- SEEK_SET: busca el absoluto desde el inicio del archivo
Puede usar SEEK_CUR para mover el puntero del archivo hacia adelante por sizeof (index).
Una vez obtenido el tamaño y la posición de los datos, solo queda recuperarlos.
Aquí, use fsetpos () debido al tipo de index.pos que es fpos_t. Una forma alternativa es usar ftell en lugar de fgetpos y fsek en lugar de fgetpos. El par fseek y ftell funcionan con int, mientras que fgetpos y fsetpos usan fpos_t.
Después de leer el registro en la memoria, se agrega un carácter nulo \ 0 para convertirlo en un cuerda C. No lo olvides o tendrás un accidente. Como antes, se llama a fclose en ambos archivos. Aunque no perderá ningún dato si olvida fclose (a diferencia de las escrituras), tendrá una pérdida de memoria.