Programación
Manejo de archivos (Material extraído en parte de
[Link] )
Los datos que hemos tratado hasta el momento han residido en la memoria principal. Sin embargo, las grandes
cantidades de datos se almacenan normalmente en un dispositivo de memoria secundaria. Estas colecciones
de datos se conocen como archivos (antiguamente ficheros).
Un flujo (stream) es una abstracción que se refiere a un flujo o corriente de datos que fluyen entre un origen y
un destino. Entre el origen y el destino debe existir una conexión o canal por la que circulen los datos. La
apertura de un archivo supone establecer la conexión del programa con el dispositivo que contiene el archivo,
por el canal que comunica el archivo con el programa van a fluir las secuencias de datos.
Un archivo es un conjunto de datos estructurados en una colección de entidades elementales o básicas
denominadas registros que son de igual tipo y constan a su vez de diferentes entidades de nivel más bajos
denominadas campos.
Hay dos tipos de archivos, archivos de texto y archivos binarios. Un archivo de texto es una secuencia de
caracteres organizadas en líneas terminadas por un carácter de nueva línea. En estos archivos se pueden
almacenar canciones, fuentes de programas, base de datos simples, etc. Los archivos de texto se caracterizan
por ser planos, es decir, todas las letras tienen el mismo formato y no hay palabras subrayadas, en negrita, o
letras de distinto tamaño o ancho.
Un archivo binario es una secuencia de bytes que tienen una correspondencia uno a uno con un dispositivo
externo. Así que no tendrá lugar ninguna traducción de caracteres. Además, el número de bytes escritos
(leídos) será el mismo que los encontrados en el dispositivo externo. Ejemplos de estos archivos son
fotografías, imágenes, texto con formatos, archivos ejecutables (aplicaciones), etc.
En C, un archivo es un concepto lógico que puede aplicarse a muchas cosas desde archivos de disco hasta
terminales o una impresora. Se asocia una secuencia con un archivo específico realizando una operación de
apertura. Una vez que el archivo está abierto, la información puede ser intercambiada entre éste y el
programa.
Se puede conseguir la entrada y la salida de datos a un archivo a través del uso de la biblioteca de funciones; C
no tiene palabras claves que realicen las operaciones de E/S. Para ello, se utiliza la librería stdio.h. A
continuación se muestra un programa básico que copia el contenido de un archivo en otro nuevo:
#include<stdio.h>
int main(){
FILE *fp1,*fp2;
char c;
char entrada[100],salida[100];
printf("Ingrese el nombre del archivo origen:\n");
scanf("%s",entrada);
if((fp1=fopen(entrada,"r"))==NULL){
printf("\nError al abrir el archivo %s",entrada);
return 0;
}
printf("Ingrese el nombre del archivo destino:\n");
scanf("%s",salida);
if((fp2=fopen(salida,"w"))==NULL){
printf("\nError al crear el archivo %s",salida);
return 0;
}
c=fgetc(fp1);
while(feof(fp1)==0){
fputc(c,fp2);
c=fgetc(fp1);
}
fclose(fp1);
fclose(fp2);
printf("\nArchivo duplicado con exito");
return 0;
}
y son punteros “especiales”, denominados punteros a archivos. El puntero a un archivo es el hilo
común que unifica el sistema de E/S con buffer. Un puntero a un archivo es un puntero a una información que
define varias cosas sobre él, incluyendo el nombre, el estado y la posición actual del archivo. En esencia
identifica un archivo específico y utiliza la secuencia asociada para dirigir el funcionamiento de las funciones de
E/S con buffer. Un puntero a un archivo es una variable de tipo puntero al tipo FILE que se define en stdio.h. Un
programa necesita utilizar punteros a archivos para leer o escribir en los mismos. Para obtener una variable de
este tipo se utiliza una secuencia como esta:
FILE *fp;
Apertura de un archivo
En el ejemplo, se usa la instrucción fopen(). La misma abre una secuencia para que pueda ser utilizada y la
asocia a un archivo. Su sintaxis es:
fp = fopen(nombre,modo)
Donde nombre es un puntero a una cadena de caracteres que representan un nombre válido del archivo y
puede incluir una especificación del directorio. La cadena a la que apunta modo determina como se abre el
archivo. La siguiente tabla muestra los valores permitidos para modo:
La función fopen() devuelve un puntero a archivo. Un programa nunca debe alterar el valor de ese puntero.
Si se produce un error cuando se esta intentando abrir un archivo, fopen() devuelve un puntero nulo (NULL).
Se puede abrir un archivo bien en modo texto o binario. En la mayoría de las implementaciones, en modo
texto, la secuencias de retorno de carro / salto de línea se convierten a caracteres de salto de línea en lectura.
En la escritura, ocurre lo contrario: los caracteres de salto de línea se convierten en salto de línea. Estas
conversiones no ocurren en archivos binarios.
La macro NULL está definida en stdio.h. Este método detecta cualquier error al abrir un archivo: como por
ejemplo disco lleno o protegido contra escritura antes de comenzar a escribir en él.
Si se usa fopen() para abrir un archivo para escritura, entonces cualquier archivo existente con el mismo
nombre se borrará y se crea uno nuevo. Si no existe un archivo con el mismo nombre, entonces se creará. Si se
quiere añadir al final del archivo entonces debe usar el modo “a”. La apertura de un archivo para las
operaciones de lectura requiere que exista el archivo; si no existe, fopen() devolverá un error. Finalmente, si
se abre un archivo para las operaciones de leer/escribir, la computadora no lo borrará si existe; sin embargo, si
no existe, la computadora lo creará.
Cierre de un archivo.
La función fclose() cierra una secuencia que fue abierta mediante una llamada a fopen(). Escribe toda la
información que todavía se encuentre en el buffer en el disco y realiza un cierre formal del archivo a nivel del
sistema operativo. Un error en el cierre de una secuencia puede generar todo tipo de problemas, incluyendo la
pérdida de datos, destrucción de archivos y posibles errores intermitentes en el programa. Su sintaxis es:
fclose(fp);
Donde es el puntero al archivo devuelto por la llamada a fopen(). Si se devuelve un valor cero significa
que la operación de cierre ha tenido éxito. Generalmente, esta función solo falla cuando un disco se ha retirado
antes de tiempo o cuando no queda espacio libre en el mismo.
La siguiente tabla da un breve resumen de las funciones que se pueden utilizar (para archivos de texto). Se
debe incluir la librería stdio.h. Observe que la mayoría de las funciones comienzan con la letra “F” (de File).
fgetc y fputc
Estas funciones son análogas a getchar() y putchar(), pero aplicadas a archivos. Estas funciones
escriben o leen un carácter de un fichero determinado por un puntero a fichero (FILE *fp).
Usando iterativamente fgetc, los caracteres se leen en forma secuencial (no se necesita usar una aritmética de
punteros para el desplazamiento).
fprintf() y fscanf()
Estas funciones se comportan exactamente como prinft() y scanf() que ya conocemos, excepto que
operan sobre archivo. La sintaxis es de la siguiente forma:
fprintf(fp, “contenido de la cadena”, variables);
fscanf(fp, “formatos”, variables);
Donde es un puntero al archivo devuelto por una llamada a fopen(). fprintf() y fscanf()
dirigen sus operaciones de E/S al archivo al que apunta .
fgets y fputs
Las funciones fgets() y fputs() pueden leer y escribir cadenas a o desde los archivos. Los prototipos de estas
funciones son:
fputs(“contenido”, fp);
fgets(“contenido”, long, fp);
La función fputs() escribe la cadena a un archivo especifico. La función fgets() lee una cadena desde el
archivo especificado hasta que lee un carácter de nueva línea o longitud-1 caracteres. Si se produce un EOF
(End of File) la función fgets() retorna un NULL.
feof()
Esta función indica con un número distinto a cero que se ha alcanzado el fin del archivo. Su sintaxis es:
feof(fp)
rewind()
La función rewind() inicializa el indicador de posición, al principio del archivo, indicado por su argumento. Su
sintaxis es:
rewind(fp)
ferror()
Determina (devolviendo un número distinto a cero) si se ha producido en error en una operación sobre un
archivo. Su sintaxis es:
ferror(fp)
fseek()
Permite el acceso aleatorio. Su sintaxis es la siguiente:
fseek(fp, offset, posición)
Con esta función, se puede considerar al archivo como una gran cadena. El valor de posición indica la
posición de referencia para el indicador de posición, y puede ser 0 (inicio), 1(actual) ó 2 (final). El valor de
offset indica el desplazamiento en relación a la posición de referencia. Un ejemplo de uso es:
fseek(fp, 2, 0);
Con esta instrucción, posicionamos el indicador a 2 lugares por delante del origen del archivo.
Para saber cuál es la posición en la que está el puntero del archivo, C proporciona la función siguiente: ftell
(fp); que devuelve la posición actual en bytes del puntero del archivo con respecto al principio del mismo.
Lectura y escritura en archivos binarios (fread y fwrite)
Para leer y escribir en archivos que no sean de texto las operaciones que se deben utilizar son fread y fwrite.
El formato de escritura en bloque es el siguiente:
fwrite (direcc_dato, tamaño_dato, numero_datos, punt_archivo);
Escribe tantos datos como indique numero de datos en el fichero, tomando los datos a partir de la dirección del
dato. Los datos tienen que tener tantos bytes como especifique tamaño. La función fwrite devuelve el
número de elementos escritos, este valor debe coincidir con numero de datos.
Para calcular el tamaño en bytes de un dato o un tipo de dato se suele utilizar la función sizeof(dato) o
sizeof(tipo-de-dato);
Por ejemplo:
int i, V[3]; --> sizeof(i) daría lo mismo que sizeof(int)
--> sizeof(V) daría 3 veces el resultado de sizeof(V[1])
Consideremos el siguiente ejemplo:
#include<stdio.h>
int main(){
int v[6],elem_escritos,i;
int car=(2+'0');
FILE *f;
f=fopen("[Link]","wb");
for(i=0;i<6;i++) v[i]=i+1+'0';
/* Para escribir los 3 últimos elementos de v (el 2, el 3 y el 4) */
elem_escritos=fwrite(&v[2],sizeof(int),3,f);
/* Para escribir el primer elemento de v, valen las 2 instrucciones siguientes */
fwrite(v,sizeof(int),1,f);
fwrite(&v[0],sizeof(int),1,f);
/* Para escribir un entero*/
fwrite(&car,sizeof(int),1,f);
return 0;
}
Pregunta: ¿Qué pasa si abrimos [Link]? ¿Por qué pasa esto?
La sentencia de lectura de bloque es la siguiente:
fread (direcc_dato, tamaño_dato, numero_datos,punt_fichero);
Lee tantos datos como indique numero de datos del fichero, colocando los datos leídos a partir de la dirección
del dato. Los datos tienen que tener tantos bytes como especifique tamaño del dato. La función fread
devuelve el número de elementos leídos, y el valor devuelto debe coincidir con numero de datos.
Ejemplo:
#include<stdio.h>
int main(){
FILE *f;
int v[6],elem_escritos,i,num;
f=fopen("[Link]","rb");
elem_escritos=fread(&v[2],sizeof(int),3,f);
fread(v,sizeof(int),1,f);
fread(&v[1],sizeof(int),1,f);
fread(&v[5],sizeof(int),6,f);
fread(&v[4],sizeof(int),5,f);
//fread(&num,sizeof(int),1,f);
//fread(&num,sizeof(num),1,f);
for(i=0;i<6;i++) printf("%d\n",v[i]);
//printf("\nnum: %d",num);
return 0;
}
Lista de ejercicios sobre archivos
1) Copiar el contenido de un archivo de texto dado a otro. Se debe solicitar un nombre del archivo de entrada,
y otro para la copia.
2) Se tiene un archivo con un conjunto de palabras. Se tiene una palabra por línea, y cada palabra tiene
menos que 50 caracteres. Generar un archivo donde dichas palabras estén ordenadas.
3) Dado un archivo de texto que contiene una matriz de enteros, convertirlo a una matriz numérica e imprimir
el resultado. Los elementos de una misma fila están separados por un salto de línea (‘\n’). Sólo se puede
usar la sentencia fgetc() para leer el archivo.
4) Diseñar un programa que permita partir un archivo en partes, donde es un entero dado por el usuario.
5) Realizar un programa que cuente el número de líneas, caracteres y palabras que tiene un archivo de texto
pasado cuya dirección se introduce por teclado.
6) Crear una estructura paciente (nombre, edad, direccion), y crear un archivo que contenga los registros de
pacientes introducidos por teclado. Hacer también lo inverso, un programa que lea un archivo e imprima
en pantalla los datos de los pacientes que contiene.
7) Obtener una raiz de la función en el intervalo [0,1], considerando
como criterio de parada un error absoluto (que es valor absoluto de la diferencia de dos aproximaciones
consecutivas y ) menor a . En un archivo de salida (llamado “[Link]”) deben almacenarse los
resultados de las iteraciones siguiendo a grandes rasgos el siguiente esquema (las líneas divisorias
representan tabulaciones y saltos de línea):
Iter Error
1 … … …
2 … … …
3 … … …
4 … … …
5 … … …
6 … … …
7 … … …
8) Realizar un programa que muestre por pantalla el contenido de un fichero fuente de lenguaje C, realizando
una traducción a pseudocódigo sencilla, según la siguiente tabla de traducción:
Lenguaje C Texto a mostrar por pantalla
{ INICIO
} FIN
while MIENTRAS
for PARA
printf MOSTRAR
scanf LEER
9) Se tiene un archivo en el siguiente formato:
Nombre//Apellido//Salario
Las personas están separadas por un salto de línea. Se debe calcular el promedio de todos los salarios de las
personas con apellidos de A a la M. Además, se debe generar un nuevo archivo donde estén todas esas
personas, con sus apellidos en mayúsculas.
Algunos ejercicios resueltos
Ejercicio 2
#include<stdio.h>
int comparar(char *c1, char *c2){
int i=0,a;
while((*(c1+i)!='\0')&&(*(c1+i)==*(c2+i))) i++;
a=*(c1+i)-*(c2+i);
return a;
}
void ordenar(char **q, int n){
int a,i,j=1,band=1;
char *t;
while(band){
band=0;
for(i=0;i<(n-j);i++){
a=comparar(*(q+i),*(q+i+1));
if(a>0){
t=*(q+i);
*(q+i)=*(q+i+1);
*(q+i+1)=t;
band=1;
}
}
j++;
}
}
int main(){
FILE *fp1,*fp2;
char c;
int i;
char entrada[100],salida[100];
printf("Ingrese el nombre del archivo origen:\n");
scanf("%s",entrada);
if((fp1=fopen(entrada,"r"))==NULL){
printf("\nError al abrir el archivo %s",entrada);
return 0;
}
printf("Ingrese el nombre del archivo destino:\n");
scanf("%s",salida);
if((fp2=fopen(salida,"w"))==NULL){
printf("\nError al crear el archivo %s",salida);
return 0;
}
int cant_pal=0;
c=fgetc(fp1);
while(!feof(fp1)){
if (c == '\n'){
cant_pal = cant_pal + 1;
}
c=fgetc(fp1);
}
rewind(fp1);
char cad[cant_pal][50];
for(i=0;i<cant_pal;i++){
fscanf(fp1,"%s",cad[i]);
}
fclose(fp1);
char *p[cant_pal];
for(i=0;i<cant_pal;i++) *(p+i)=*(cad+i);
ordenar(p,cant_pal);
//Se escribe el nuevo archivo
for(i=0;i<cant_pal;i++){
fprintf(fp2,"%s\n",*(p+i));
}
fclose(fp2);
printf("\nArchivo creado con exito");
return 0;
}
Ejercicio 3
#include<stdio.h>
int main(){
FILE *fp1;
char c;
int i,m,n,j;
char entrada[100],salida[100];
printf("Ingrese el nombre del archivo origen:\n");
scanf("%s",entrada);
if((fp1=fopen(entrada,"r"))==NULL){
printf("\nError al abrir el archivo %s",entrada);
return 0;
}
n=1;
c=fgetc(fp1);
while((!feof(fp1))&&(c!='\n')){
if(c==' ') n++;
c=fgetc(fp1);
}
rewind(fp1);
c=fgetc(fp1);
m=0;
while(!feof(fp1)){
if (c == '\n'){
m++;
}
c=fgetc(fp1);
}
rewind(fp1);
printf("\nLa cantidad de filas es: %d",m);
printf("\nLa cantidad de columnas es: %d\n",n);
int mat[m][n],num,x;
for(i=0;i<m;i++){
num=0;
x=0;
c=fgetc(fp1);
while(c!='\n'){
if(c==' '){
mat[i][x]=num;
num=0;
x++;
}
else num=num*10+(c-'0');
c=fgetc(fp1);
}
mat[i][x]=num;
}
//salida
printf("\n La matriz es:\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("%d\t",mat[i][j]);
}
printf("\n");
}
return 0;
}