MCD y MCM Objetivo: El alumno aprenderá a manejar archivos de texto en lenguaje C, leyendo la información contenida para el cálculo de el mı́nimo común múltiplo y el máximo común divisor de el conjunto de números contenido en el archivo. Desarrollo: Realice los apartados que se muestran en este documento. 1 Manejo de archivos en Lenguaje C Los archivos se almacenan en una computadora como una larga cadena de datos binarios sin significado especı́fico. Esta cadena de valores puede contener desde información inconexa y arbitraria, hasta texto, imágenes y programas. Por esta razón, al inicio de cada archivo suele incluirse un encabezado o cabecera que contiene información sobre el archivo mismo, como el tipo de contenido (imagen, video, archivo comprimido, archivo ejecutable, etcétera) y el formato en el cual vienen codificados los datos del archivo, ya que de otro modo, los datos almacenados no podrı́an ser reinterpretados para extraer la información que contienen. Sin embargo, los archivos de texto plano no suelen contener encabezados, lo que hace su lectura/escritura muy simple y útil para fines didácticos. La biblioteca estándar de entrada/salida de C incluye funciones para el manejo básico de los archivos; por lo que es responsabilidad del programador leer los archivos byte a byte en busca de la información requerida, incluyendo el desarrollo de las funciones que decodifican la información contenida en el mismo. El manejo de archivos requiere la inclusión de la biblioteca estándar de entrada/salida, por lo que es necesario referir a stdio.h en los encabezados del programa. La biblioteca contiene el siguiente conjunto de funciones referidas al manejo de archivos: • FILE *fopen(const char *path, const char* mode): Abre un archivo en el modo seleccionado y devuelve un apuntador a un tipo de estructura especial que sirve para referir al archivo abierto y que representa al archivo abierto (el apuntador es cero si el archivo no pudo ser abierto). Sus parámetros son los siguientes: – path: La ruta o nombre de archivo a abrir. – mode: Modo de apertura que indica los permisos de acceso (lectura, escritura, adjuntado, lectura y escritura, escritura y creación, adjuntado y actualización). • fclose(FILE *stream): Cierra el acceso al archivo apuntado por stream, liberándolo para que otros procesos puedan accesarlo. • feof(FILE *stream): Informa si la cabeza de lectura ha alcanzado el final del archivo apuntado por stream. • fscanf(FILE *stream, const char *format, ... ): Lee datos del archivo apuntado por stream de la misma forma en que scanf lo hace de la consola (entrada estándar). • fprintf():. Escribe datos al archivo apuntado por stream de la misma forma en que printf lo hace de la consola (salida estándar). • char *fgets(char *str, int num, FILE *stream): Lee caracteres del archivo apuntado por stream y los almacena en str hasta que (num-1) caracteres han sido leı́dos, se encuentra una nueva lı́nea o se alcanza el final del archivo, lo que suceda primero. Si tiene éxito, devuelve str, o NULL en caso de que se presente algún error. Página 1 de 4 • int fputs(const char *str, FILE *stream): Volca el contenido de str en el archivo apuntado por stream, devolviendo EOF en caso de error. • int fgetc(FILE *stream): Devuelve el caracter apuntado por la cabeza de lectura en la estructura *stream y avanza la cabeza de lectura para que apunte al siguiente caracter del archivo apuntado por stream. • void fputc(int c, FILE *stream): Escribe el caracter c en el archivo apuntado por stream y avanza la cabeza de lectura. • long int ftell(FILE *stream): Devuelve el valor de la cabeza de lectura/escritura de la estructura *stream. • int ftell(FILE *stream, long int offset, int origin): Mueve la cabeza de lectura/escritura de la estructura *stream a la posición indicada por offset con base en el valor de origin, que puede tomar los valores SEEK SET para el inicio del archivo, SEEK CUR para la posición actual y SEEK END para el final del archivo. El siguiente programa en lenguaje C, abre un archivo de texto e imprime todo su contenido en la consola, luego muestra el número de lı́neas leı́das, letras y dı́gitos. 1 #include <stdio.h> 2 3 4 5 6 7 void main() { char c; FILE *file; int alpha = 0, num = 0, lines = 1; 8 if((file = fopen("example.txt", "r")) == NULL) { printf("File not found"); return; } while(!feof(file)) { c = fgetc(file); if( ((c >= ’A’) && (c <= ’Z’)) || ((c >= ’z’) && (c <= ’z’))) ++alpha; else if((c >= ’0’) && (c <= ’9’)) ++num; else if(c == ’\n’) ++lines; putc(c); } fclose(file); printf("Alpha: %d\r\nNum: %d\r\nLines: %d\r\n", alpha, num, lines); 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 } Página 2 de 4 2 Mı́nimo común múltiplo y máximo común divisor Se define al mı́nimo común múltiplo de un conjunto de números naturales de la siguiente forma: Definición 1. Sea x̄ = {x1 , x2 , ..., xn |xi ∈ N}. Se define a n ∈ N el mı́nimo común múltiplo (abreviado M.C.M), de x̄ como el menor número natural que es múltiplo de todas las componentes de x̄. Por otra parte, se define al máximo común divisor de un conjunto de números naturales de la siguiente forma: Definición 2. Sea x̄ = {x1 , x2 , ..., xn |xi ∈ N}. Se define a n ∈ N el máximo común divisor (abreviado M.C.D), de x̄ como el mayor número natural que divide a todas las componentes de x̄ sin dejar residuo. La siguiente función calcula el máximo común divisor de dos números: 1 2 3 4 5 int mcd2(int a, int b) { int i = 2; int mcd = 1; char moda, modb; 6 while((a > 1) && (b > 1)) { moda = a % i; modb = b % i; if(!moda && !modb) mcd*= i; if(!moda) a /= i; if(!modb) b /= i; if(moda && modb) ++i; } return mcd; 7 8 9 10 11 12 13 14 15 16 17 } Página 3 de 4 3 Especificaciones del programa Desarrolle un programa en lenguaje C que calcule el mı́nimo común múltiplo y el máximo común divisor de todos los números enteros de un archivo de texto y que cumpla con los siguientes requisitos: • El programa recibe como argumento el nombre del archivo a leer. • El programa lee hasta 5 números enteros de no más de 5 dı́gitos del archivo, cada uno separado por espacio. • El programa escribe en pantalla los números leı́dos del archivo separados por comas como primera lı́nea, y luego en las siguientes lı́neas el mcm y el MCD. • [Código limpio] Las funciones de cálculo de mcm y MCD, ası́ como las de análisis de datos de archivo están en archivos separados (es necesario separar las cabeceras del código fuente). • [Robustez] El programa da un aviso si no se provee un archivo de texto válido. • [Robustez] El programa puede leer los números aún cuando estos estén separados por más de un espacio, caracter de tabulador, comas o comas y espacios. • [Robustez] El programa utiliza los primeros N números encontrados (si los hay) o de lo contrario avisa que el archivo de entrada no contiene números. • [Punto extra] El programa puede leer N números (manejo dinámico de memoria). A continuación se muestra un programa principal ejemplo, main.c: 1 2 3 4 // main.c #include <stdio.h> // Required for writing to console with printf #include <fileparser.h> // Contains the function read_numbers(...) #include <mcm_mcd.h> // Contains the functions mcm(...) and mcd(...) 5 6 7 8 9 void main(int argc, char **argc) { int i num_count; int* numbers; 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // Validate the input files if((argc < 2) || !read_numbers(argc[1], &numbers, &num_count)) { printf("Invalid input file"); return; } // Print out the numbers for(i = 0; i < num_count; ++i) printf("%d, "numbers[i]); // Print out the mcm. The ’\b’ is to delete the last written comma printf("\b\r\nmcm: %d", mcm(numbers, num_count)); // Print out the MCD printf("\r\nMCD: %d", mcd(numbers, num_count)); } // End of main Archivo de texto de ejemplo, numbers.txt 1 12 48 96 128 1024 4096 Página 4 de 4