- Aprender
- Informática
- Programación
- Programación en C
- Entrada y salida de archivos en C
Entrada y salida de archivos en C
fgets. Guarda arrays y registros en archivos binarios con fread y fwrite, diseña formatos de archivo que se lean correctamente en otras máquinas y accede directamente a cualquier registro con fseek. En todo momento, comprueba cada resultado, explica los fallos con errno y libera los recursos para que tus programas fallen de forma segura e informen fielmente del resultado.PrincipianteUnidad 7 de 8 del curso Curso de Programación en C
Lo que aprenderás
- Explicar cómo los programas en C acceden a los archivos mediante flujos con almacenamiento en búfer, abrir y cerrar archivos en el modo adecuado y utilizar los tres flujos estándar con redirección en la línea de comandos.
- Explicar los flujos, el almacenamiento en búfer y las diferencias entre el modo texto y el modo binarioDescribir qué son un flujo y un
FILE *, cómo funcionan el almacenamiento en búfer y la posición en el archivo, y en qué se diferencia el modo texto del modo binario en Windows y en sistemas similares a Unix. - Abrir y cerrar archivos con fopen y fcloseAbrir un archivo en el modo adecuado para la tarea, comprobar que se abrió, localizar archivos mediante rutas y cerrar cada flujo al terminar.
- Utilizar stdin, stdout y stderr con redirecciones y tuberíasUtilizar los tres flujos con los que comienza todo programa en C, enviar los resultados y mensajes de error al lugar adecuado y permitir que los usuarios los redirijan desde la línea de comandos.
- Explicar los flujos, el almacenamiento en búfer y las diferencias entre el modo texto y el modo binario
- Procesar archivos de texto carácter a carácter, mediante lecturas y escrituras con formato y línea a línea, eligiendo el patrón seguro para cada caso y comprobando cada resultado.
- Leer y escribir carácter a carácterUtilizar fgetc y fputc con una variable int, detenerse correctamente al llegar al fin de archivo y contar, copiar y transformar texto carácter a carácter.
- Escribir y leer texto con formato mediante fprintf y fscanfEscribir números y cadenas en archivos con una disposición elegida, construir nombres de archivo con sprintf y leer datos con formato mediante fscanf comprobando cuántos elementos convirtió.
- Leer líneas de forma segura con fgetsLeer texto una línea a la vez con fgets, gestionar saltos de línea, líneas largas y finales de línea de Windows, y analizar cada línea después de leerla.
- Leer y escribir carácter a carácter
- Escribir y leer bytes en bruto, arrays y estructuras con fwrite y fread, y diseñar formatos de archivo binarios que se lean correctamente en otras máquinas.
- Leer y escribir datos binarios con fread y fwriteUtilizar fread y fwrite con el tamaño y el número de elementos adecuados, copiar archivos por bloques, reconocer tipos de archivo por sus primeros bytes y almacenar arrays y estructuras.
- Diseñar formatos binarios que funcionen en distintas máquinasExplicar cómo los tamaños de los tipos, el orden de los bytes y el relleno de las estructuras hacen que los archivos binarios en bruto dependan de la máquina, y leer y escribir campos de varios bytes en un formato definido.
- Leer y escribir datos binarios con fread y fwrite
- Obtener y establecer la posición en el archivo para acceder directamente a los datos que necesitas y actualizar registros de tamaño fijo en su misma posición sin reescribir todo el archivo.
- Obtener y establecer la posición en el archivo con ftell, fseek y rewindObtener la posición actual, desplazarse a posiciones relativas al principio, al lugar actual o al final, calcular desplazamientos de registros y tamaños de archivos, y conocer las limitaciones de los flujos de texto.
- Actualizar registros de tamaño fijo en su misma posiciónAbrir un archivo tanto para lectura como para escritura, seguir la regla para alternar entre ambas y modificar, añadir o eliminar registros sin corromper el archivo.
- Obtener y establecer la posición en el archivo con ftell, fseek y rewind
- Comprobar el resultado de cada operación con archivos, explicar los fallos al usuario con errno y liberar todos los recursos para que los programas fallen de forma segura e informen fielmente del éxito.
- Comprobar cada resultado y distinguir el fin de archivo de los erroresConocer cómo señala un fallo cada función de stdio, usar feof y ferror cuando termina un bucle de lectura y detectar errores de escritura que solo aparecen al escribir el contenido del búfer.
- Explicar los fallos con errno, perror y strerrorUtilizar errno para averiguar por qué falló una llamada a la biblioteca e imprimir mensajes de error claros que indiquen el programa, el archivo y el motivo.
- Liberar los recursos e informar del resultadoCerrar todos los flujos en todas las rutas, no dejar archivos de salida escritos a medias y devolver un estado de salida que refleje fielmente el resultado.
- Comprobar cada resultado y distinguir el fin de archivo de los errores
Guía de estudio de Entrada y salida de archivos en C
1Abrir archivos como flujos y conectar los programas con su entrada y su salida
Los archivos llegan a un programa a través de flujos, que controlan el acceso, el almacenamiento en búfer y la posición. La elección de un modo puede conservar los datos antiguos, borrarlos o forzar cada escritura al final, por lo que la apertura forma parte del diseño del programa. Los tres flujos estándar extienden la misma interfaz a terminales, archivos redirigidos y tuberías. Comprender sus conexiones te permite escribir una rutina de procesamiento que funcione en varios entornos y mantenga los mensajes de diagnóstico separados de los resultados útiles.
1.1 Explicar los flujos, el almacenamiento en búfer y las diferencias entre el modo texto y el modo binario
Un archivo contiene datos almacenados; un flujo es el canal que utiliza un programa en ejecución para acceder a ellos. Las mismas funciones de flujo también pueden trabajar con un terminal o una tubería. Incluye <stdio.h> y guarda el puntero al flujo en una variable de tipo FILE *.
Qué información conserva el flujo
El objeto FILE de la biblioteca lleva el control de un búfer, la posición actual en el archivo y los indicadores de fin de archivo y de error. Pasa su puntero a funciones como fgetc y fprintf; no inspecciones ni modifiques sus campos. Una lectura o escritura adelanta la posición hasta después de los datos procesados. En un archivo binario que comienza con "hello\nworld\n", leer los primeros seis bytes sitúa la siguiente lectura en la w.
El almacenamiento en búfer agrupa pequeñas operaciones en transferencias más grandes. Una escritura que tiene éxito puede significar que los datos llegaron a un búfer de memoria y que la transferencia al sistema operativo sigue pendiente. fflush(fp) envía la salida pendiente al sistema operativo. fclose(fp) hace eso antes de liberar el flujo. Ninguna de las dos garantiza que los datos sobrevivan a un corte repentino de electricidad.
| Modo de almacenamiento en búfer | Cuándo se transfiere la salida |
|---|---|
| Búfer completo | Cuando se llena el búfer, o cuando se vacía o cierra explícitamente |
| Búfer por línea | También cuando se escribe un salto de línea |
| Sin búfer | Lo antes posible |
Los archivos ordinarios abiertos con fopen usan búfer completo. Los dispositivos interactivos pueden usar otro modo. Un terminal o una tubería no ofrecen posiciones de archivo ordinarias para desplazarse hacia atrás y hacia delante.
Elegir el modo texto o binario
El modo texto maneja líneas legibles por personas. El modo binario conserva los valores de los bytes sin transformaciones de texto. Añade b al modo de apertura para datos binarios: "rb" los lee y "wb" los escribe. Las imágenes, los datos comprimidos y los registros en bruto necesitan el modo binario, aunque una versión en modo texto parezca funcionar en tu propio equipo.
En Linux y macOS, un salto de línea es un byte LF, 0x0A, y los dos modos normalmente se comportan igual. En Windows, la salida en modo texto transforma '\n' en los dos bytes CR LF, 0x0D 0x0A. La entrada en modo texto transforma CR LF de nuevo en un solo '\n'. La entrada en modo texto de Microsoft también reconoce 0x1A, Ctrl+Z, como fin de archivo.
Por ejemplo, escribir "a\nb\nc\n" en modo texto produce seis bytes en Linux o macOS y nueve en Windows. Tres letras aportan tres bytes; tres finales de línea de Windows aportan seis. Leer el archivo de Windows en modo texto en Windows da seis caracteres, mientras que el modo binario expone los nueve bytes.
Distinguir los bytes del archivo de su texto
Una línea creada en Windows y leída en Linux puede conservar "\r\n". Eliminar solo '\n' deja '\r', lo que puede hacer que falle una comparación con "yes". Una posición en un flujo de texto también puede reflejar el estado de la transformación, así que guarda y restaura las posiciones de texto en lugar de calcular desplazamientos en bytes.
La garantía de C de recuperar exactamente el texto escrito se aplica a caracteres imprimibles, tabulaciones y saltos de línea, sin espacios inmediatamente antes de un salto de línea y con un salto de línea al final de la última línea. Otras secuencias de bytes pertenecen a flujos binarios. Los bytes de C tienen al menos ocho bits; los ejemplos con valores de byte de 0 a 255 y formatos de archivo habituales usan bytes de ocho bits.
Puntos clave
- Un
FILE *da a las funciones de biblioteca acceso a un flujo y a su estado. - La salida almacenada en búfer puede fallar al vaciar el búfer o cerrar el flujo; comprueba esos resultados.
- El modo texto de Windows transforma los finales de línea; el modo binario conserva los bytes.
- Guarda las posiciones de texto; calcula desplazamientos en archivos binarios.
- Explicita el supuesto de bytes de ocho bits al interpretar formatos binarios habituales.
1.2 Abrir y cerrar archivos con fopen y fclose
Elige un modo de apertura antes de escribir cualquier código de procesamiento. El modo controla si el archivo debe existir, si se conserva su contenido y dónde se realizan las escrituras. fopen actúa sobre el archivo inmediatamente: una apertura con "w" que tiene éxito borra un archivo existente aunque no se escriba nada después.
Elegir el modo adecuado para la tarea
| Modo | Archivo existente | Archivo inexistente | Acceso |
|---|---|---|---|
"r" | Conserva el contenido | Falla | Lectura desde el principio |
"w" | Borra el contenido | Crea el archivo | Escritura desde el principio |
"a" | Conserva el contenido | Crea el archivo | Escritura al final |
"r+" | Conserva el contenido | Falla | Lectura y escritura |
"w+" | Borra el contenido | Crea el archivo | Lectura y escritura |
"a+" | Conserva el contenido | Crea el archivo | Lectura en cualquier posición; escritura al final |
Añade b para el modo binario; "r+b" y "rb+" significan lo mismo. C11 añade la creación exclusiva con x en los modos de escritura, como "wx" y "wbx": la apertura falla si el archivo existe. Usa una cadena de modo documentada; las letras de modo adicionales no son portables.
Comprobar antes de usar el puntero
fopen devuelve un puntero a un flujo nuevo si tiene éxito y NULL si falla. Los archivos inexistentes, las rutas inaccesibles y los directorios inexistentes pueden causar el fallo.
FILE *fp = fopen("scores.txt", "r");
if (fp == NULL) {
fprintf(stderr, "Cannot open scores.txt\n");
return EXIT_FAILURE;
}
/* Read through fp here. */
if (fclose(fp) == EOF) {
fprintf(stderr, "Cannot close scores.txt\n");
return EXIT_FAILURE;
}
El fragmento utiliza <stdio.h> y <stdlib.h>. Las funciones como fgetc, fprintf y fclose necesitan un flujo válido; pasar NULL produce comportamiento indefinido. fflush(NULL) es una excepción deliberada que vacía los búferes de todos los flujos de salida.
fclose devuelve 0 si tiene éxito y EOF si falla. Una vez llamada, el flujo queda cerrado, incluso cuando el cierre informa de un error. No vuelvas a llamar a fclose con el mismo puntero ni lo uses para más operaciones de entrada o salida. Cierra los archivos sin demora: los flujos disponibles son limitados y C garantiza la disponibilidad de al menos ocho flujos abiertos, incluidos los tres estándar.
Resolver rutas desde el programa en ejecución
Una ruta relativa como "data/input.txt" parte del directorio de trabajo actual, no de la carpeta del archivo fuente. Iniciar el programa en otro lugar cambia el destino de esa ruta. Las barras inclinadas también funcionan con la biblioteca de C de Microsoft. Una barra invertida en una cadena de C debe duplicarse, como en "C:\\data\\in.txt"; de lo contrario, una secuencia de escape como \n puede cambiar la cadena.
Para nombres proporcionados en la línea de comandos, usa int main(int argc, char *argv[]). En un comando de copia con dos nombres, argc debe ser 3, argv[1] es el nombre de entrada y argv[2] es el nombre de salida. Abre primero la entrada para que un nombre de entrada mal escrito no cree ni borre la salida. Si falla la apertura de la salida, cierra la entrada ya abierta. Usa archivos de entrada y salida distintos: abrir primero la entrada no la protege si el nombre de salida identifica ese mismo archivo.
Un retorno normal de main o exit cierra los flujos, pero los cierres explícitos permiten que el programa detecte errores de salida diferidos. Un cierre inesperado puede hacer que se pierdan datos almacenados en búfer; abort no ofrece la garantía de liberación de recursos de una salida normal.
Puntos clave
- Abrir con
"w"borra inmediatamente un archivo existente. - Comprueba si
fopendevuelveNULLantes de usar el flujo. - Las rutas relativas parten del directorio de trabajo actual.
- Cierra cada flujo abierto una sola vez y nunca lo reutilices después de
fclose. - Abre la entrada de una copia antes que la salida y usa archivos distintos.
- Los modos de escritura exclusivos de C11 fallan cuando el destino existe.
1.3 Utilizar stdin, stdout y stderr con redirecciones y tuberías
Un programa en C comienza con tres flujos abiertos: la entrada estándar stdin, la salida estándar stdout y la salida de error estándar stderr. Normalmente se conectan al terminal, pero el intérprete de comandos puede conectarlos a archivos u otros programas. Puedes pasarlos directamente a las funciones de flujo.
Mantener los resultados separados de los mensajes de diagnóstico
printf escribe en stdout; scanf lee de stdin. getchar() lee un carácter de stdin y putchar(c) escribe uno en stdout. puts(s) escribe una cadena en stdout y añade un salto de línea. Sus formas para flujos permiten que el mismo código funcione con un archivo:
void print_total(FILE *out, int total)
{
fprintf(out, "Total: %d\n", total);
}
Llamar a print_total(stdout, 12) imprime un resultado; pasar un puntero a un archivo abierto dirige la misma disposición a un archivo. En código de producción, haz que la función auxiliar informe de un fallo de escritura a la función llamadora.
Escribe los mensajes de diagnóstico con fprintf(stderr, ...). Así, un programa de procesamiento de datos puede producir un archivo de resultados limpio y explicar los fallos por separado. Un error impreso en stdout puede corromper los datos que otro programa espera leer.
Dejar que el intérprete de comandos elija las conexiones
En un intérprete de comandos similar a Unix, este comando separa los tres flujos:
./sortnums < numbers.txt > sorted.txt 2> errors.txt
< proporciona la entrada, > crea o sustituye la salida, >> añade salida al final y 2> redirige los errores. Los operadores de redirección también funcionan en el símbolo del sistema de Windows, donde el nombre del programa suele escribirse como sortnums.exe. Una tubería, como en generate | sortnums, envía la salida estándar del primer programa a la entrada estándar del segundo. De forma predeterminada, no transporta stderr.
Un filtro puede aceptar un nombre de entrada opcional y, en caso contrario, leer de stdin. Lleva el control de la propiedad: cierra un flujo que tu función haya abierto, pero deja un flujo prestado para quien lo posee. Para un filtro sencillo de línea de comandos, if (in != stdin) identifica el archivo que abrió por sí mismo.
Vaciar el búfer de los mensajes antes de esperar la entrada
Un terminal suele hacer que stdout use búfer por línea. Un archivo redirigido o una tubería suelen hacer que use búfer completo. stderr no usa búfer completo. Esto explica por qué puede cambiar el orden de la salida cuando ambos flujos se envían a un mismo destino.
Después de printf("Saving..."), el texto puede permanecer en el búfer porque no hay un salto de línea. Usa fflush(stdout) y comprueba si devuelve EOF cuando deba aparecer antes de la siguiente operación. Vaciar el búfer envía la salida pendiente; no lee ni descarta la entrada. El estándar de C no define fflush(stdin) como una operación para limpiar la entrada. Para descartar una línea, lee caracteres hasta '\n' o EOF.
Si un programa imprime dos líneas en stdout y una advertencia en stderr entre ellas, un terminal suele mostrarlas en el orden del programa. Con > out.txt 2>&1, la advertencia puede llegar al archivo antes que la salida estándar almacenada en búfer. No uses el orden de visualización de flujos mezclados como registro del orden de ejecución.
Devolver un resultado utilizable a los scripts
Devuelve EXIT_SUCCESS o 0 cuando toda la tarea tenga éxito y EXIT_FAILURE cuando falle. Ambas macros están declaradas en <stdlib.h>. Un comando del intérprete como convert in.csv out.txt && echo done ejecuta el segundo comando solo después de un resultado satisfactorio. Un estado de salida fiel al resultado importa tanto como un mensaje de diagnóstico útil.
Puntos clave
- Usa
stdin,stdoutystderrsin abrirlos. - Envía los resultados del programa a
stdouty los mensajes de diagnóstico astderr. - La redirección cambia las conexiones; una tubería lleva la salida estándar a la entrada estándar.
- Vacía el búfer de salida cuando un mensaje de solicitud deba verse inmediatamente.
- Devuelve un estado de salida que indique fallo cuando falle la tarea solicitada.
2Leer y escribir archivos de texto
El procesamiento de texto tiene tres escalas útiles: caracteres individuales, valores con formato y líneas completas. La elección cambia lo que el programa puede validar y cómo se recupera de una entrada incorrecta. Un bucle de caracteres debe distinguir los bytes del fin de archivo; una lectura con formato debe comprobar su número de asignaciones; un lector de líneas debe gestionar la capacidad y los registros incompletos. Los ejemplos resueltos muestran cómo conservar los datos realmente leídos mientras se mantienen bajo control el análisis posterior y los mensajes.
2.1 Leer y escribir carácter a carácter
La entrada y salida de caracteres es útil cuando cada carácter determina qué ocurre después: contar separadores, cambiar entre mayúsculas y minúsculas o reconocer un formato de texto sencillo. fgetc lee un carácter y fputc escribe uno. Ambas están declaradas en <stdio.h>.
Guardar el valor de retorno en un int
fgetc(fp) devuelve el carácter como un unsigned char convertido a int, o la constante negativa EOF cuando no hay ningún carácter disponible porque terminó la entrada o falló una lectura. En los sistemas habituales con bytes de ocho bits, los valores válidos de un byte van de 0 a 255. Un int mantiene esos valores separados de EOF.
int c;
while ((c = fgetc(in)) != EOF) {
if (fputc(c, out) == EOF) {
/* Report a write failure and clean up. */
break;
}
}
/* Check ferror(in) before declaring the input complete. */
Un char sin modificar puede perder esta distinción. Con char con signo, el byte 0xFF puede convertirse en -1 y parecer un fin de archivo. Con char sin signo, el valor negativo EOF se convierte en un byte y nunca puede ser igual a EOF en una comparación.
Conserva ambos paréntesis alrededor de la asignación. En c = fgetc(in) != EOF, la comparación se realiza primero, por lo que c recibe 1 o 0 en lugar del carácter. getc realiza la misma lectura y puede ser una macro; fputc devuelve el carácter escrito o EOF.
Contar lecturas realizadas, no intentos
La condición del bucle debe realizar la lectura. feof describe un intento anterior que llegó al final; no puede predecir el siguiente intento. Un bucle que comprueba !feof(in) y después cuenta incondicionalmente añade una pasada extra al final.
Para el texto "hi\nyo\n", seis lecturas realizadas dan seis caracteres y dos caracteres de salto de línea. Contar solo los caracteres de salto de línea funciona para líneas terminadas en salto de línea. El texto "red\ngreen\nblue" tiene dos saltos de línea, pero tres líneas, porque el "blue" final sigue siendo una línea.
Para incluir una línea final sin salto de línea, lleva también el control del último carácter:
size_t lines = 0;
int c, last = '\n';
while ((c = fgetc(in)) != EOF) {
if (c == '\n') ++lines;
last = c;
}
if (last != '\n') ++lines;
Inicializar last con '\n' hace que un archivo vacío cuente como cero líneas. Un error de lectura sigue requiriendo una ruta de fallo separada; un recuento parcial no es un resultado completo.
Transformar o examinar el siguiente carácter
toupper de <ctype.h> puede convertir letras antes de escribirlas. El resultado de una lectura correcta con fgetc ya está en el rango de entrada válido para toupper, por lo que fputc(toupper(c), out) es seguro dentro del bucle. Los valores arbitrarios de char con signo necesitan convertirse a unsigned char antes de pasarse a funciones de clasificación de caracteres.
ungetc(c, in) devuelve un carácter al flujo para que la siguiente lectura lo devuelva de nuevo. Se garantiza la devolución de un carácter; comprueba si se devuelve EOF cuando falle la devolución. Esto ayuda a un analizador a examinar el siguiente carácter sin perderlo. Una operación de posicionamiento descarta el carácter devuelto.
El recuento describe lo que proporciona el modo de flujo seleccionado. En Windows, el modo texto cuenta un par CR LF como un solo '\n'; el modo binario expone ambos bytes. Por tanto, un recuento de caracteres mediante un flujo de texto no tiene por qué coincidir con el recuento de bytes almacenados.
Puntos clave
- Guarda los resultados de
fgetcenintpara distinguir los valores de bytes deEOF. - Comprueba el resultado de cada lectura antes de procesarlo o contarlo.
- Cuenta como una línea una parte final no vacía sin salto de línea.
- Se garantiza la devolución de un carácter con
ungetc; el posicionamiento lo descarta.
2.2 Escribir y leer texto con formato mediante fprintf y fscanf
La entrada y salida con formato convierte entre valores de C y texto legible. fprintf escribe con la disposición que elijas; fscanf lee valores que coinciden con un formato. Sus especificaciones de conversión se parecen, pero la entrada requiere punteros a los tipos de destino adecuados.
Escribir valores legibles
Usa %d para un int, %ld para un long, %zu para un size_t, %s para una cadena y %c para un carácter. En la salida, %f imprime un double; un argumento float se promociona a double. %% imprime un signo de porcentaje.
int result = fprintf(fp, "%-8s%6.2f\n", "Oslo", 3.14159);
if (result < 0) {
/* Handle a write failure. */
}
%-8s alinea "Oslo" a la izquierda en ocho columnas; %6.2f alinea "3.14" a la derecha en seis. La línea resultante es "Oslo 3.14\n", y el valor de retorno es 15 caracteres. El ancho de campo es un mínimo, no un límite de longitud de la salida. La precisión también cambia la información almacenada: %.2f escribe 3.14159 como "3.14", por lo que esos dígitos adicionales no pueden recuperarse del archivo.
Un programa sencillo que escribe CSV puede producir "Lima,2024,19.5" con un orden fijo y comas entre campos. El texto que contiene comas, comillas o saltos de línea necesita el entrecomillado de campos CSV. Encierra ese campo entre comillas dobles y duplica cualquier comilla que haya en su interior. Escribir con un %s sin más solo es adecuado cuando los campos de texto no contienen esos caracteres.
Dar formato en un array de tamaño limitado
sprintf no tiene un argumento para el tamaño del destino. Dar formato a "007.jpg" necesita ocho bytes incluido el terminador nulo. Es preferible usar snprintf:
char name[32];
int n = snprintf(name, sizeof name, "%03d.jpg", 7);
if (n < 0 || (size_t)n >= sizeof name) {
/* Formatting failed or the full result did not fit. */
}
El valor de retorno no negativo es la longitud que necesitaría el texto completo, sin contar el terminador nulo. Un valor igual al tamaño del búfer ya significa que se ha producido truncamiento. Comprueba si el resultado es negativo antes de convertirlo a size_t.
Leer el número esperado de valores
char name[20];
int age;
int matched = fscanf(fp, "%19s %d", name, &age);
Esta llamada solo tiene éxito completo si devuelve 2. El ancho 19 deja un byte para el terminador. El nombre del array proporciona su dirección; el entero necesita &age. Para la entrada, %f necesita float *, mientras que %lf necesita double *. Una incompatibilidad produce comportamiento indefinido, no una conversión inofensiva.
La mayoría de las conversiones omiten los caracteres en blanco iniciales, incluidos los saltos de línea. Por tanto, fscanf no exige un registro por línea y %s no puede leer "New York" como un solo valor. Un conjunto de exploración como %19[^,] lee hasta una coma; para los registros suele ser más claro leer líneas completas.
Con "10 abc 20", %d primero almacena 10 y devuelve 1. La siguiente llamada devuelve 0 porque "abc" no coincide; ese texto permanece sin leer. Comprobar != EOF repite el fallo indefinidamente. Comprueba == 1 y después gestiona los datos no válidos por separado del fin de la entrada o de un error del flujo. El resultado de una conversión es un recuento de valores asignados, no un valor de verdad para todo el archivo.
Puntos clave
- Haz coincidir cada especificación de formato con el tipo de argumento.
- Limita el ancho de
%sen la entrada al tamaño del array menos uno. - Compara el resultado de
fscanfcon el número exacto de asignaciones esperadas. snprintfdevuelve la longitud completa necesaria; un valor de retorno al menos igual a la capacidad indica truncamiento.- Los campos de texto CSV necesitan comillas cuando contienen comas, comillas o saltos de línea.
- La salida con formato de coma flotante puede perder precisión.
2.3 Leer líneas de forma segura con fgets
Leer una línea completa antes de analizarla mantiene separado cada registro de texto. Si un registro no es válido, puedes informar de su número de línea y continuar con el siguiente. fgets limita cuánto escribe, pero aún debes gestionar los saltos de línea que conserva y las líneas más grandes que el array.
Leer en un espacio de capacidad conocida
char line[256];
while (fgets(line, sizeof line, fp) != NULL) {
/* Process this successful read. */
}
if (ferror(fp)) {
/* Report an interrupted read. */
}
fgets lee como máximo un carácter menos que la capacidad, se detiene después de un salto de línea si encuentra uno y añade un terminador nulo. Devuelve el búfer si tiene éxito. Un resultado NULL significa que no había caracteres disponibles al final del archivo o que se produjo un error; no proceses el búfer después de un error de lectura.
sizeof line proporciona la capacidad del array en este ámbito. En una función auxiliar cuyo parámetro es char *line, es el tamaño del puntero. Pasa la capacidad real por separado. La antigua función gets no puede limitar la entrada y se eliminó en C11.
Reconocer las partes de líneas largas
Con char buf[8], "temperature\n" llega como "tempera" y después "ture\n". La primera llamada usa siete bytes para caracteres y uno para el terminador. Una lectura correcta sin salto de línea podría ser una parte de una línea larga o la parte final sin terminación al final del archivo.
Elige una estrategia: unir las partes en un registro más grande, procesarlas como una secuencia continua o rechazar el registro y descartar todas sus partes restantes. Para aplicar un límite de tamaño, examina el siguiente carácter antes de rechazar un búfer exactamente lleno. Un salto de línea a continuación completa el registro; EOF sin un error del flujo completa una línea final sin salto de línea; otro carácter indica que hay más contenido. Devuelve un carácter al flujo solo si el código posterior necesita leerlo de nuevo.
Eliminar solo un final que se haya encontrado
Después de decidir si el registro está completo, elimina los finales de línea:
line[strcspn(line, "\r\n")] = '\0';
strcspn de <string.h> encuentra el primer carácter de los indicados, o el terminador existente si no aparece ninguno. Esto maneja tanto un CR LF conservado como un LF. Eliminar line[strlen(line) - 1] incondicionalmente puede borrar datos reales de una línea final sin salto de línea y no es válido para una cadena vacía.
fputs(line, out) escribe la cadena sin añadir un salto de línea; puts(line) escribe en stdout y añade uno. Una copia de texto puede pasar cada parte leída correctamente por fgets a fputs, comprobando si falla, incluso cuando una línea larga ocupa varias partes. Para obtener exactamente los bytes originales, usa entrada y salida binaria por bloques.
Analizar y validar por separado
sscanf(line, "%d %9s", &qty, item) devuelve 2 para "7 apples" y 0 para "seven apples". Un análisis fallido no deja ningún token incorrecto atascado en el flujo de entrada porque la línea ya se leyó. Las conversiones numéricas con sscanf siguen requiriendo una entrada representable; usa strtol para una comprobación estricta del rango.
Para strtol, establece errno en cero, pasa un puntero al final de la conversión y comprueba si no hay dígitos, si se produjo ERANGE y si hay texto adicional inesperado. Con "42abc", el resultado 42 es solo un prefijo numérico. Para un campo de número entero, permite los caracteres en blanco finales que hayas elegido y exige después el final de la cadena. Comprueba el rango del tipo de destino antes de convertir un long válido a int.
Puntos clave
fgetsconserva un salto de línea cuando cabe y siempre reserva espacio para un terminador si tiene éxito.- Un búfer lleno sin salto de línea requiere comprobar si la línea está completa.
- Pasa las capacidades explícitamente a las funciones auxiliares que usan punteros.
- Usa
strcspnpara eliminar solo un final de línea que exista. - Lee primero y después analiza y valida cada registro.
- Comprueba con
strtollos dígitos, los errores de rango y los datos adicionales.
3Guardar y cargar datos en archivos binarios
Los archivos binarios transfieren los bytes de los objetos sin convertirlos en texto legible. Los tamaños y números de elementos determinan cómo se manejan los arrays completos, los registros y los bloques finales parciales. Un archivo compartido entre máquinas también necesita una representación explícita: anchos de campo, orden de los bytes y disposición. Leer una firma es solo el comienzo de la comprobación de esa representación; las longitudes y los recuentos deben validarse antes de que el programa confíe en los datos restantes o reserve espacio para ellos.
3.1 Leer y escribir datos binarios con fread y fwrite
La entrada y salida binaria transfiere los bytes de los objetos sin convertirlos en números o palabras legibles. fread y fwrite reciben una dirección, un tamaño de elemento, un número de elementos y un flujo. Sus valores de retorno cuentan elementos completos, por lo que la elección del tamaño de elemento determina qué puedes deducir del resultado.
Escribir y leer arrays
int a[5] = {1, 2, 3, 4, 5};
if (fwrite(a, sizeof a[0], 5, out) != 5) {
/* Handle a write failure. */
}
En un sistema con int de cuatro bytes, esto escribe 20 bytes y devuelve 5. Leer en otro array de cinco elementos con la misma representación permite recuperar los valores. Usa "wb" y "rb" para que las transformaciones de texto no alteren los bytes.
Para un solo objeto, pasa su dirección: fwrite(&rec, sizeof rec, 1, out). Para objetos a los que se accede mediante un puntero p, usa fwrite(p, sizeof *p, count, out). sizeof p mide el puntero y &p apunta a la propia variable de tipo puntero. Ninguno expresa la transferencia de datos deseada. Cuando el tamaño o el número de elementos es cero, ambas funciones devuelven cero sin transferir datos.
Copiar exactamente los bytes leídos
unsigned char buf[512];
size_t n;
while ((n = fread(buf, 1, sizeof buf, in)) > 0) {
if (fwrite(buf, 1, n, out) != n) {
/* Handle failure and stop copying. */
break;
}
}
Un tamaño de elemento de 1 hace que n sea un recuento de bytes. Una entrada de 1300 bytes proporciona 512, 512 y 276 bytes, y después cero. La última escritura debe usar 276, no la capacidad de 512. Escribir todo el búfer añade 236 bytes sobrantes de lecturas anteriores.
Con fread(buf, 512, 1, in), la misma entrada devuelve 1, 1 y después 0. Los últimos 276 bytes son solo un elemento parcial; no se cuentan y el valor de ese elemento parcial es indeterminado. Elige elementos de un byte cuando sea útil un bloque final parcial. Elige elementos del tamaño de un registro cuando deban rechazarse los registros incompletos.
Una lectura corta de fread puede indicar fin de archivo o un error. Comprueba ferror después de copiar y comprueba fclose en la salida: una escritura aceptada en un búfer puede fallar más tarde. Una cabecera o un registro parcial nunca debe tratarse como un objeto completo.
Examinar firmas y objetos almacenados
Usa unsigned char para bytes en bruto; en sistemas con bytes de ocho bits contiene valores de 0 a 255 sin valores negativos. Los datos JPEG comienzan con 0xFF 0xD8. Las fotos de cámaras y teléfonos suelen continuar con un marcador APPn, lo que produce el patrón de recuperación desde FF D8 FF E0 hasta FF D8 FF EF. Ese patrón es una prueba útil para identificar posibles archivos; valida el formato antes de confiar en él. Algunos archivos JPEG usan un segmento siguiente diferente.
Una estructura con char name[20] y un int score suele ocupar 24 bytes. Una escritura en bruto incluye todos sus bytes almacenados, incluido el relleno. Un miembro de tipo puntero guarda una dirección, no la cadena a la que apunta. Usa un array dentro de la estructura o escribe por separado la longitud y los caracteres de la cadena.
El texto legible puede ocupar más espacio: "1000000\n" usa ocho bytes en Linux y nueve en modo texto de Windows. Un entero de 32 bits codificado explícitamente usa cuatro bytes de ocho bits. El almacenamiento de objetos en bruto es compacto, pero vincula el archivo a la representación utilizada por quien lo creó.
Puntos clave
freadyfwritedevuelven recuentos de elementos completos, que no siempre son recuentos de bytes.- Usa un tamaño de elemento de 1 cuando importe un bloque final parcial.
- Escribe la cantidad leída, no toda la capacidad del búfer.
- Un miembro de tipo puntero almacenado en bruto contiene una dirección, no los datos a los que apunta.
- Comprueba las transferencias cortas y el cierre de la salida.
- Usa las firmas para identificar posibles archivos y después valida el archivo.
3.2 Diseñar formatos binarios que funcionen en distintas máquinas
Un flujo binario conserva los bytes, pero dos máquinas pueden interpretar los mismos bytes de forma distinta. Un formato portable define qué significa cada byte en lugar de adoptar la disposición que el compilador dé a un objeto.
Especificar tamaños, orden y disposición
Tres aspectos hacen que un volcado de objetos en bruto dependa de la máquina:
| Aspecto | Qué puede variar |
|---|---|
| Tamaño del tipo | int puede ocupar dos o cuatro bytes; long suele diferir entre Windows y Linux de 64 bits |
| Orden de los bytes | El byte menos o más significativo puede ir primero |
| Disposición de la estructura | El relleno y los desplazamientos de los miembros pueden cambiar |
El orden con el byte menos significativo primero almacena primero ese byte. El orden con el byte más significativo primero almacena primero ese byte. El valor 258, en hexadecimal 0x00000102, es 02 01 00 00 con el byte menos significativo primero y 00 00 01 02 con el byte más significativo primero.
Una disposición habitual de una estructura con un char seguido de un int ocupa ocho bytes: un byte para el carácter, tres bytes de relleno y cuatro para el entero. Los valores del relleno no están especificados. Los tipos de ancho exacto de <stdint.h>, como uint32_t, fijan los anchos en bits cuando están disponibles; no fijan el orden de los bytes ni eliminan el relleno. Los ejemplos de formatos de archivo habituales presuponen bytes de ocho bits y la disponibilidad de esos tipos.
Decodificar los campos explícitamente
Lee cuatro bytes completos antes de interpretarlos. Para un campo con el byte menos significativo primero:
uint32_t v = (uint32_t)b[0]
| (uint32_t)b[1] << 8
| (uint32_t)b[2] << 16
| (uint32_t)b[3] << 24;
Aquí b es un array de bytes sin signo. Cada conversión explícita de tipo se realiza antes del desplazamiento, de modo que la operación aritmética tiene el ancho sin signo necesario. Para codificar, extrae los ocho bits menos significativos, después los ocho siguientes y así sucesivamente. Comprueba cada operación de salida. Escribir v directamente con fwrite volvería a introducir el orden de los bytes de la máquina en el formato.
Definir un pequeño formato completo
Un formato de puntuaciones puede usar "SCR1" como firma de cuatro bytes, un recuento de cuatro bytes con el byte menos significativo primero y registros compuestos por 20 bytes para el nombre más cuatro bytes para la puntuación con el byte menos significativo primero. La firma también identifica la versión. La cabecera tiene ocho bytes; cada registro tiene exactamente 24 bytes.
Cinco registros ocupan 128 bytes. El registro con índice 3 comienza en el byte 80: se omiten ocho bytes de cabecera y tres registros completos. Indica la codificación del campo de nombre y la política de terminación o relleno como parte de un formato real; un campo de nombre fijo de 20 bytes no es automáticamente una cadena de C.
Comprueba la longitud completa y la firma de una cabecera antes de usar sus campos. Después comprueba los recuentos y las longitudes frente a límites razonables y a los datos disponibles antes de reservar memoria. Un recuento plausible no basta si faltan registros posteriores.
Leer la estructura real del formato
Algunos archivos PCM WAV tienen una cabecera sencilla de 44 bytes, con la frecuencia de muestreo en los bytes 24 a 27. Los bytes 44 AC 00 00 se decodifican como 44 100 muestras por segundo. Otros archivos WAV contienen bloques adicionales, por lo que un lector general sigue los identificadores y tamaños de los bloques para localizar el bloque fmt . Suponer una disposición de cabecera fija para todos los archivos puede hacer que se lea un campo sin relación como si fuera la frecuencia.
El texto como CSV evita problemas de tamaño de los enteros, orden de los bytes y relleno de estructuras, y es fácil de examinar. Sigue necesitando una disposición de campos definida, reglas de entrecomillado y un análisis comprobado. Elígelo cuando la legibilidad y el intercambio importen más que una representación binaria compacta.
Puntos clave
- Un formato binario portable define los anchos de campo, el orden de los bytes y los desplazamientos.
- Los tipos enteros de ancho exacto por sí solos no hacen portable un volcado de una estructura.
- Convierte los bytes al ancho sin signo previsto antes de desplazarlos.
- Valida toda la cabecera y limita los recuentos antes de confiar en ellos.
- Sigue la estructura de bloques cuando un formato permita bloques adicionales.
- Un campo de texto de ancho fijo necesita una política explícita de codificación y longitud.
4Desplazarse a cualquier posición de un archivo
Las posiciones en el archivo hacen posible el acceso directo cuando los registros tienen ubicaciones conocidas. Calcular esas ubicaciones exige prestar atención a las cabeceras, los anchos de los registros y los índices que empiezan en cero. Las posiciones de texto tienen reglas más estrictas porque la transformación puede cambiar su significado. Cuando un flujo admite tanto lectura como escritura, el posicionamiento también controla los cambios válidos de dirección. Por tanto, actualizar un registro combina el cálculo de una ubicación con una secuencia comprobada que conserva los datos circundantes.
4.1 Obtener y establecer la posición en el archivo con ftell, fseek y rewind
Las lecturas secuenciales recorren los datos en orden. El acceso aleatorio permite que un programa salte a una ubicación conocida, como un registro en un archivo grande. El posicionamiento solo funciona cuando el flujo subyacente lo admite, y el significado de una posición difiere entre flujos binarios y de texto.
Obtener y cambiar la posición
ftell(fp) devuelve una posición de tipo long o -1L si falla. En un archivo binario, una posición cuenta los bytes desde su inicio. fseek(fp, offset, whence) se desplaza con respecto a un punto de referencia seleccionado y devuelve 0 si tiene éxito y un valor distinto de cero si falla.
| Referencia | Significado del desplazamiento |
|---|---|
SEEK_SET | Desde el principio |
SEEK_CUR | Desde la posición actual |
SEEK_END | Desde el final |
Por ejemplo, fseek(fp, -4L, SEEK_END) solicita los últimos cuatro bytes de un archivo binario en un sistema que admite desplazamientos relativos al final. Comprueba el resultado antes de leer. Una solicitud sobre una tubería o un terminal, o una solicitud anterior al principio de un archivo ordinario, puede fallar.
Calcular la ubicación de un registro
Para registros binarios de tamaño fijo sin cabecera, el índice que empieza en cero multiplicado por el ancho del registro da el desplazamiento inicial. Con registros de 24 bytes, el índice 3 comienza en el byte 72. Después de leer todo ese registro, la posición es 96.
long offset = 3L * (long)sizeof(struct record);
if (fseek(fp, offset, SEEK_SET) != 0) {
/* Handle the failed move before reading. */
}
Este ejemplo presupone que el archivo se escribió con la misma disposición de estructura. En cambio, un formato portable especificado usa el ancho de registro definido en él. Incluye los bytes de cabecera cuando los haya. El tercer registro contado desde 1 tiene índice 2, no índice 3. Para índices grandes, valida la multiplicación y asegúrate de que el resultado cabe en long antes de desplazarte.
En los sistemas habituales, desplazarse al final y usar ftell permite medir un archivo binario: un archivo de 120 bytes con registros de 24 bytes contiene cinco registros. Comprueba ambas llamadas y restaura la posición después. El estándar de C no exige que SEEK_END tenga un significado útil para todos los flujos binarios, por lo que el método depende de que ese flujo lo admita.
Guardar posiciones de texto sin operaciones aritméticas
El modo texto puede transformar los finales de línea. Una posición de texto devuelta es información para volver a un lugar, no un recuento al que sumar o restar. Un desplazamiento de texto distinto de cero usa un valor devuelto anteriormente por ftell para el mismo archivo, con SEEK_SET. Se permite un desplazamiento de cero con cualquier punto de referencia.
rewind(fp) vuelve al principio y borra tanto el indicador de fin de archivo como el de error. No devuelve ningún resultado, por lo que debes usar fseek(fp, 0L, SEEK_SET) y comprobarlo cuando sea necesario confirmar que el desplazamiento tuvo éxito. Una llamada a fseek que tiene éxito borra el indicador de fin de archivo y descarta el carácter devuelto con ungetc, pero deja activado cualquier indicador de error existente.
Conservar un valor de posición
fgetpos(fp, &pos) guarda una posición en fpos_t; fsetpos(fp, &pos) la restaura. Comprueba si ambas devuelven un valor distinto de cero que indique fallo. Trata fpos_t como un valor opaco, sin operaciones aritméticas. Puede contener información de posición que va más allá de un simple recuento de bytes.
fseek y ftell usan long, que suele tener 32 bits en Windows y limita las posiciones representables a unos 2 GiB. Un valor de posición evita vincular la interfaz a long, pero el tamaño de archivo admitido sigue dependiendo de la implementación y la configuración.
Puntos clave
- Comprueba si
fseekdevuelve un valor distinto de cero y siftelldevuelve-1L. - El desplazamiento de un registro incluye la cabecera y todos los registros anteriores.
- Usa de forma coherente índices que empiecen en cero.
- Restaura las posiciones de texto con valores guardados, sin operaciones aritméticas sobre desplazamientos.
rewindborra ambos indicadores; una llamada afseekque tiene éxito borra el de fin de archivo.- El posicionamiento requiere un flujo que admita desplazamientos y desplazamientos representables.
4.2 Actualizar registros de tamaño fijo en su misma posición
Un flujo de actualización lee y escribe el mismo archivo. Usa "r+b" para conservar un archivo binario existente mientras modificas sus registros. "w+b" borra inmediatamente el archivo antiguo y los modos de adición al final fuerzan cada escritura al final.
Realizar cambios de dirección válidos
Un flujo de actualización tiene dos reglas de alternancia:
| Cambio | Acción requerida entre operaciones |
|---|---|
| De escritura a lectura | Vaciar el búfer de salida o realizar una operación de posicionamiento |
| De lectura a escritura | Realizar una operación de posicionamiento, salvo que la lectura haya llegado al fin de archivo |
Las funciones de posicionamiento incluyen fseek, fsetpos y rewind. Comprueba las funciones que devuelven un resultado antes de continuar. Un cambio de dirección sin la acción requerida produce comportamiento indefinido; no puedes predecir que solo afectará al siguiente registro.
Actualizar un registro completo
Supón que un archivo sin cabecera contiene cinco registros de 24 bytes con la misma representación que struct record. Para modificar el índice 3:
- Abre con
"r+b"y comprueba el puntero. - Desplázate al byte 72 y comprueba que la operación tuvo éxito.
- Lee exactamente un registro completo.
- Cambia su miembro
scorea 99. - Vuelve al principio del registro.
- Escribe exactamente un registro y comprueba el resultado del cierre.
Las operaciones intermedias clave son:
if (fread(&rec, sizeof rec, 1, fp) != 1) {
/* Report the incomplete input, then clean up. */
goto cleanup;
}
rec.score = 99;
if (fseek(fp, -(long)sizeof rec, SEEK_CUR) != 0) {
/* Report the failed seek, then clean up. */
goto cleanup;
}
if (fwrite(&rec, sizeof rec, 1, fp) != 1) {
/* Report the failed write, then clean up. */
goto cleanup;
}
La función que contiene este código proporciona una etiqueta cleanup que cierra su flujo y devuelve un fallo en estas ramas. Después de la lectura, la posición es 96. Retroceder 24 bytes selecciona el registro original y permite pasar de lectura a escritura. Una sustitución correcta deja el archivo con 120 bytes y cinco registros. Restaurar una posición inicial guardada es otra forma válida de realizar la actualización.
Decidir cuándo reescribir
Añadir un registro de 24 bytes aumenta ese archivo a 144 bytes. Desplazarse al principio de un flujo "ab" o "a+b" no puede convertir su siguiente escritura en una sobrescritura; la escritura sigue realizándose en el final actual.
Una sustitución en la misma posición debe caber en el espacio que sustituye. Cambiar "Bo,7" por "Bo,9" cabe. Escribir "Bo,12" sobre "Bo,7\n" consume el salto de línea y une el texto siguiente al registro. Para ampliar o reducir registros, escribe un archivo nuevo corregido. El estándar de C no tiene ninguna función de flujo que elimine un intervalo de bytes del medio de un archivo. En su lugar, la eliminación puede copiar todos los registros que se conservan o marcar un registro de tamaño fijo como eliminado.
Mantener recuperable la sustitución
Comprueba remove y rename; ambas devuelven 0 si tienen éxito. Que rename sustituya un destino existente es un comportamiento definido por la implementación. Linux y macOS admiten la sustitución; la biblioteca de C de Microsoft falla cuando el destino existe.
Un plan de recuperación portable escribe y cierra el archivo nuevo, cambia el nombre del original por un nombre de copia de seguridad distinto y cambia el nombre del archivo nuevo por el nombre original. Si falla la instalación, conserva la copia de seguridad disponible para restaurarla e informa de dónde está. Elimina la copia de seguridad solo después de que la instalación tenga éxito. Esta secuencia permite la recuperación; no es una transacción atómica portable ni una garantía frente a un corte de electricidad.
Puntos clave
- Usa
"r+b"para actualizar un archivo existente sin truncarlo. - Sigue la regla de alternancia entre lectura y escritura incluso cuando la posición deseada parezca obvia.
- Vuelve a la posición anterior después de leer un registro y antes de sustituirlo.
- Las escrituras en modo de adición siempre van al final.
- Reescribe los datos cuando cambie su longitud.
- Comprueba cada paso de la sustitución y conserva un original recuperable.
5Detectar errores de archivo, informar de ellos y recuperarse
Una operación con archivos puede fallar después de que la apertura haya tenido éxito, y la salida almacenada en búfer puede fallar solo al cerrar. Los programas fiables leen los valores de retorno, distinguen los datos no válidos de los problemas de acceso, conservan información útil sobre los errores y liberan los recursos en todas las rutas. Estas tareas funcionan conjuntamente: el mensaje de diagnóstico debe explicar el fallo original, mientras que el estado final también debe reflejar los fallos detectados durante la liberación de recursos. Un programa de copia completo muestra cómo encajan estas decisiones en el flujo de control habitual de C.
5.1 Comprobar cada resultado y distinguir el fin de archivo de los errores
Un archivo puede abrirse correctamente y aun así fallar durante la lectura, la escritura, el posicionamiento o el cierre. Un programa robusto comprueba el resultado de la operación que realmente intentó realizar. Distingue una lectura completa de una entrada interrumpida y los datos mal formados de un fallo del flujo.
Conocer cada señal de fallo
| Operación | Señal que debe gestionarse |
|---|---|
fopen, fgets | NULL |
fgetc, fputc, fflush, fclose | EOF |
fprintf | Un resultado negativo |
fputs | EOF |
fread, fwrite | Menos elementos completos de los solicitados |
fscanf | Menos asignaciones de las esperadas, o EOF |
fseek, fgetpos, fsetpos | Un resultado distinto de cero |
ftell | -1L |
rewind, clearerr y perror no devuelven ningún valor. Si es necesario comprobar que el desplazamiento al principio tuvo éxito, usa fseek en lugar de confiar en un resultado de rewind.
Examinar el flujo después de que se detenga una lectura
El flujo mantiene indicadores separados de fin de archivo y de error. feof(fp) comprueba el primero; ferror(fp) comprueba el segundo. Cada una devuelve un valor distinto de cero si su indicador está activado.
while (fgets(line, sizeof line, fp) != NULL) {
/* Process successful input. */
}
if (ferror(fp)) {
fprintf(stderr, "Input could not be completed\n");
/* Return failure after cleanup. */
}
El valor devuelto por fgets por sí solo no distingue los dos motivos de detención. Del mismo modo, fgetc devuelve EOF y fread devuelve un recuento corto tanto al llegar al fin de archivo como al fallar la lectura. No compruebes feof antes de leer para predecir si quedan datos.
Los indicadores permanecen activados. clearerr borra ambos, igual que rewind. Una llamada a fseek o fsetpos que tiene éxito borra el indicador de fin de archivo, pero no un error existente. Borrar un indicador reconoce su estado; no vuelve a conectar una unidad retirada ni repara un archivo dañado. Vuelve a intentarlo solo cuando se haya solucionado la causa.
Distinguir los datos no válidos de un fallo de acceso
Si fscanf(fp, "%d", &value) devuelve 0 con "abc", la conversión falló antes de cualquier asignación. No es necesario que se haya llegado al fin de archivo ni que haya un error de lectura. Informa de que el campo no es válido, preferiblemente con un número de línea. Un mensaje sobre el disco describiría un problema equivocado.
Si una cabecera obligatoria se lee como un elemento y fread devuelve 0, la cabecera está incompleta. No examines ese objeto parcialmente lleno. Si ferror está activado, informa de un error de entrada; si el archivo terminó, informa de datos truncados. Un archivo vacío y una cabecera parcial pueden producir el mismo recuento de elementos, por lo que el formato esperado establece el requisito de que la cabecera exista.
Detectar fallos de salida diferidos
Un disco puede llenarse después de que el programa haya abierto su salida. Las escrituras almacenadas en búfer pueden parecer correctas hasta que fflush o fclose transfieren realmente los bytes pendientes. Comprueba el cierre final y permite que convierta una tarea que, por lo demás, tuvo éxito en un fallo. No vuelvas a cerrar el mismo flujo después.
En un terminal, Ctrl+D al principio de una línea en Linux o macOS, o Ctrl+Z y después Enter en Windows, suelen señalar el fin de la entrada. clearerr(stdin) borra el indicador resultante si el programa necesita otro intento. La acción en el terminal y un error de lectura siguen siendo causas distintas.
Puntos clave
- Comprueba cada función según su propio valor de retorno documentado.
- Lee primero y después usa los indicadores del flujo para identificar el motivo de la detención.
- Una falta de coincidencia de formato indica datos no válidos, no necesariamente un error del flujo.
- Rechaza las cabeceras y los registros obligatorios incompletos.
- Un error diferido al cerrar la salida debe hacer que la tarea falle.
5.2 Explicar los fallos con errno, perror y strerror
Un valor de retorno te indica que una operación ha fallado. Un código de error puede ayudar a explicar por qué. Usa errno solo en el contexto del comportamiento documentado de la operación y consérvala antes de que otras llamadas a la biblioteca puedan cambiarla.
Dejar que la operación indique el fallo
errno es una expresión int modificable de <errno.h>, con duración de almacenamiento local a cada hilo en C11. Empieza en cero. Las llamadas a la biblioteca no la restablecen a cero y una llamada que tiene éxito puede dejar o establecer un valor distinto de cero. Por tanto, comprobar errno después de una llamada a fopen que tuvo éxito no indica que la apertura haya fallado.
En sistemas POSIX, una llamada fallida a fopen establece un código de error. La biblioteca de C de Microsoft también proporciona códigos de error de archivo. El estándar de C no exige que fopen establezca errno. Para informar de forma portable, se puede poner a cero antes de la apertura y usar un mensaje general si no se proporcionó ningún código:
errno = 0;
FILE *fp = fopen(name, "r");
if (fp == NULL) {
int saved = errno;
if (saved != 0)
fprintf(stderr, "Cannot open %s: %s\n",
name, strerror(saved));
else
fprintf(stderr, "Cannot open %s\n", name);
return EXIT_FAILURE;
}
Este fragmento también necesita <stdio.h>, <stdlib.h> y <string.h>. Guardar el código inmediatamente evita que llamadas posteriores para informar del error o liberar recursos sustituyan el motivo original. Un código guardado es útil incluso si el mensaje final se imprime mucho más tarde.
Elegir una función de mensajes
perror(label) escribe la etiqueta, dos puntos y un espacio, el mensaje de error actual y un salto de línea en stderr. Un mensaje típico de archivo inexistente es "missing.txt: No such file or directory"; la redacción exacta depende del sistema.
strerror(code) devuelve el mensaje de la biblioteca para un código proporcionado, por lo que sirve para un mensaje de diagnóstico personalizado que indique el programa, la operación y el archivo. No modifiques la cadena devuelta. Usa strerror(saved) después de liberar los recursos en lugar de consultar el nuevo valor de errno.
C define EDOM, ERANGE y EILSEQ. Los códigos de archivo habituales como ENOENT, EACCES, EEXIST y ENOSPC los proporcionan POSIX y bibliotecas de C ampliamente disponibles. Adapta tu código a la interfaz y la plataforma de destino. No supongas que los valores numéricos de los códigos ni el texto exacto de los mensajes en inglés son universales.
Gestionar un valor de retorno ambiguo
Para algunas funciones, el resultado por sí solo no puede distinguir el éxito de un error de rango. strtol puede devolver LONG_MAX como número válido o como resultado de un desbordamiento. Pon errno a cero antes de la conversión y después examina el código de rango documentado:
errno = 0;
char *end;
long value = strtol(text, &end, 10);
if (end == text) {
/* No digits were converted. */
} else if (errno == ERANGE) {
/* The value is outside the long range. */
} else {
/* Validate trailing characters and the target range. */
}
Un mensaje de diagnóstico útil proporciona al usuario información con la que actuar. Indica el archivo y la acción intentada; conserva el motivo del sistema cuando esté disponible. Envíalo a stderr y deja stdout disponible para datos de resultados válidos.
Puntos clave
- Usa el resultado de la función para determinar que ha fallado antes de interpretar
errno. - Guarda
errnoinmediatamente antes de las llamadas para informar del error o liberar recursos. perrorusa el valor actual deerrno;strerroracepta un código guardado.- Pon
errnoa cero antes destrtoly compruebaERANGE. - Incluye el archivo y la acción intentada en un mensaje de diagnóstico.
5.3 Liberar los recursos e informar del resultado
La gestión de errores solo está completa cuando se liberan los recursos y el programa informa del resultado real. Planifica la propiedad cuando abras un flujo y haz que todas las rutas posteriores respeten ese plan.
Cerrar una vez cada flujo abierto
Si falla la apertura de la entrada, no hay ningún flujo que cerrar. Si falla la apertura de la salida después de que la entrada se haya abierto correctamente, cierra la entrada. Si la copia falla después de abrir ambos, cierra ambos, comprueba el cierre de la salida y conserva un estado de fallo. Cerrar un puntero dos veces no es una protección adicional; usa un flujo ya cerrado.
El código con retornos anticipados suele cerrar los recursos en el orden inverso al de su adquisición. Una etiqueta compartida para liberar recursos es útil cuando varias rutas de fallo necesitan el mismo trabajo. Inicializa los punteros en NULL, mantén un estado de fallo de forma predeterminada y establece el éxito solo después de que termine la fase de procesamiento.
El siguiente programa completo copia archivos de entrada y salida distintos en modo binario. Usa mensajes generales porque el estándar de C no exige que todos los fallos de stdio proporcionen errno.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *in = NULL, *out = NULL;
int status = EXIT_FAILURE;
int output_acquired = 0;
unsigned char buf[512];
size_t n;
if (argc != 3) {
fprintf(stderr, "Usage: %s input output\n", argv[0]);
return EXIT_FAILURE;
}
in = fopen(argv[1], "rb");
if (in == NULL) {
fprintf(stderr, "Cannot open input: %s\n", argv[1]);
goto cleanup;
}
out = fopen(argv[2], "wb");
if (out == NULL) {
fprintf(stderr, "Cannot open output: %s\n", argv[2]);
goto cleanup;
}
output_acquired = 1;
while ((n = fread(buf, 1, sizeof buf, in)) > 0) {
if (fwrite(buf, 1, n, out) != n) {
fprintf(stderr, "Write failed: %s\n", argv[2]);
goto cleanup;
}
}
if (ferror(in)) {
fprintf(stderr, "Read failed: %s\n", argv[1]);
goto cleanup;
}
status = EXIT_SUCCESS;
cleanup:
if (out != NULL && fclose(out) == EOF) {
fprintf(stderr, "Close failed: %s\n", argv[2]);
status = EXIT_FAILURE;
}
if (in != NULL && fclose(in) == EOF) {
fprintf(stderr, "Close failed: %s\n", argv[1]);
status = EXIT_FAILURE;
}
if (output_acquired && status != EXIT_SUCCESS) {
if (remove(argv[2]) != 0)
fprintf(stderr, "Cannot remove incomplete output: %s\n",
argv[2]);
}
return status;
}
El bucle de entrada procesa solo los bytes realmente leídos. Una escritura corta salta a la liberación de recursos; se comprueba si hay un error de entrada después del bucle. El cierre de la salida aún puede convertir el éxito en fallo. Solo se intenta eliminar la salida cuando esta ejecución la abrió correctamente, por lo que un fallo de apertura no provoca la eliminación de un archivo ajeno. El requisito de usar archivos distintos es importante: "wb" borraría la entrada si los dos nombres identificaran el mismo archivo.
Conservar los datos existentes importantes
Abrir directamente la salida no permite deshacer el truncamiento. Para sustituir datos valiosos, escribe primero con un nombre temporal distinto. Comprueba cada escritura y el cierre final antes de cambiar los nombres. Conserva el original con un nombre de copia de seguridad hasta que el archivo nuevo esté instalado; comprueba cada llamada a rename y remove. Si la instalación falla, informa de la ubicación del archivo recuperable en lugar de eliminarlo.
Una función auxiliar debe cerrar lo que haya abierto y devolver un resultado de éxito o fallo a la función llamadora. La función llamadora puede decidir si vuelve a intentarlo o termina. Devolver EXIT_FAILURE después de cualquier paso incompleto permite que un script se detenga antes de una acción dependiente, como eliminar el original después de una copia de seguridad.
Puntos clave
- Inicializa la propiedad de los flujos y mantén el fallo como estado predeterminado.
- Cierra cada flujo abierto correctamente exactamente una vez en cada ruta.
- Deja que un fallo de cierre cambie el estado final.
- Elimina la salida incompleta solo cuando esta ejecución la haya creado o abierto.
- Escribe las sustituciones por separado y conserva el original hasta que la instalación tenga éxito.
- Las funciones auxiliares informan de los fallos a sus funciones llamadoras para que estas puedan elegir cómo recuperarse.
Ejemplos de Entrada y salida de archivos en C
1Abrir un flujo sin perder datos existentes
12 pasos
- 1IntroducciónDecidir qué debe conservarse
Un programa está a punto de guardar resultados. Antes de abrir nada, decide si deben conservarse los datos antiguos y si el archivo contiene líneas legibles o bytes exactos. Esas decisiones determinan el modo, y una elección equivocada puede borrar datos antiguos.
Seguirás una escritura desde el texto del programa a través de su flujo (el canal de la biblioteca hacia un archivo o terminal) y después elegirás dónde deben ir los resultados normales y los mensajes de diagnóstico (los flujos estándar). La apertura ya es una operación sobre el archivo, por lo que esta decisión precede a la primera escritura.
- 2ExplicaciónAcceder a los datos mediante un flujo
Un flujo es el canal de la biblioteca estándar hacia un archivo, un terminal o una tubería. Guarda su referencia como un
FILE *de<stdio.h>y pásala a funciones comofprintf.El objeto de la biblioteca recuerda el búfer, la posición y los indicadores de estado. No modifiques sus campos. La posición en un archivo avanza a medida que se procesan los datos; un terminal o una tubería no pueden tratarse como un archivo ordinario para desplazarse por él.
- 3EjemploSeguir los saltos de línea hasta los bytes almacenados
Escribir
"a\nb\nc\n"proporciona tres letras y tres caracteres de salto de línea. En Linux o macOS, el archivo de texto contiene seis bytes. En Windows, cada salto de línea se convierte en CR LF, lo que da nueve bytes almacenados.Leer ese archivo de Windows en modo texto de Windows recupera seis caracteres. La entrada binaria expone los nueve bytes. El texto del programa y la secuencia de bytes almacenada son dos formas de ver las mismas líneas.
- 4ConexiónElegir un modo para una imagen
Una secuencia de bytes en una imagen comprimida puede contener valores que el modo texto transforma. Añadir
bselecciona el modo binario y conserva esos valores.Esto explica por qué un lector de imágenes que funciona con
"r"en Linux puede fallar en Windows. Debe usar"rb"porque maneja una secuencia definida de bytes, no líneas que la biblioteca deba transformar. - 5DesafíoComprobar un recuento de caracteres
En Windows, una escritura en modo texto almacena
"m\nn\n". ¿Cuántos bytes contiene el archivo resultante y por qué?---reveal---
Contiene seis bytes: dos letras más dos pares CR LF. Los cuatro caracteres proporcionados por el programa se amplían porque cada salto de línea se convierte en dos bytes almacenados. En modo binario, esos mismos cuatro bytes proporcionados se escribirían sin esa transformación.
- 6ExplicaciónDecidir qué conservar antes de acceder
Para un archivo existente,
"r"permite leer,"w"borra y permite escribir, y"a"conserva los datos y añade al final. Un+permite tanto la entrada como la salida.Usa
"r+"para conservar un archivo existente al actualizarlo."w+"sigue borrándolo."wx"de C11 falla si el archivo existe, lo que resulta útil cuando debe protegerse un destino existente. - 7ConsejoAbrir y cerrar un flujo
Después de
FILE *fp = fopen(name, "r");, compruebafp == NULLantes de cualquier operación sobre el flujo. Un fallo no proporciona ningún flujo que procesar o cerrar.Si tiene éxito, el programa posee un flujo que necesita una llamada a
fclose. Comprueba también ese cierre. El flujo queda cerrado después de la llamada incluso si el cierre falla; volver a intentar cerrarlo usa un flujo que ya no es válido. - 8DesafíoProteger el registro anterior
Un archivo de registro existente debe conservar sus entradas y hay que añadir una línea nueva. Una persona que está aprendiendo elige
"w"porque la tarea escribe. ¿Qué modo debe usarse y cuándo destruiría"w"las entradas antiguas?---reveal---
Usa
"a". El modo"w"destruye el contenido existente en cuanto la apertura tiene éxito, incluso antes de una escritura. La elección debe proteger el registro durante la apertura, no solo durante el código de procesamiento posterior. - 9ExplicaciónSeparar los resultados de las explicaciones
Los flujos ya abiertos son
stdinpara la entrada,stdoutpara los resultados ystderrpara los mensajes de diagnóstico. Una función que aceptaFILE *outpuede escribir la misma disposición en un archivo o enstdout.Un intérprete de comandos puede redirigir la salida estándar a un archivo o enviarla por una tubería a otro programa. Mantén el texto de diagnóstico en
stderrpara que un resultado legible por una máquina no incluya mensajes de error entre sus filas de datos. - 10Idea claveHacer que aparezca el texto almacenado en búfer
La salida puede esperar en un búfer de memoria. La salida estándar a un terminal suele usar búfer por línea; la salida redirigida suele usar búfer completo. La salida de error estándar no usa búfer completo.
Un mensaje de solicitud sin salto de línea puede necesitar una llamada a
fflush(stdout)cuyo resultado se compruebe antes de que comience la entrada. Vaciar el búfer no descarta la entrada. Para omitir una línea de entrada no deseada, consume caracteres hasta su salto de línea o el fin de archivo. - 11DesafíoSeguir la salida de un filtro
Un filtro lee la entrada estándar, escribe resultados en la salida estándar y escribe errores en la salida de error estándar. Se ejecuta como
filter < raw.txt > clean.txt. ¿Dónde debe aparecer un mensaje sobre un registro rechazado y qué estado debe devolver si no puede completar la tarea?---reveal---
El mensaje de diagnóstico va al destino de error heredado, normalmente el terminal. Devuelve
EXIT_FAILURE. El archivo de resultados permanece separado del mensaje y el intérprete de comandos puede detectar el fallo. - 12RepasoGestionar conscientemente el ciclo de vida de un flujo
Elige qué conservar y el comportamiento de texto o binario antes de abrir. Comprueba que la apertura haya producido un flujo, procesa solo mediante punteros válidos y cierra una vez cada flujo que poseas.
Los flujos estándar permiten que el intérprete de comandos proporcione las conexiones. Separa los resultados de los mensajes de diagnóstico y devuelve un estado que describa la tarea completada. Estas decisiones permiten que el mismo programa pequeño funcione de forma interactiva, con archivos o dentro de una cadena de programas conectados por tuberías.
2Leer un registro de texto y validar su contenido
15 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónLeer a la escala adecuada
- 2ExplicaciónDistinguir un carácter de EOF
- 3EjemploContar solo una lectura correcta
- 4DesafíoIncluir la línea final sin terminación
- 5EjemploEscribir con una disposición controlada
- 6ConsejoReservar espacio para el terminador de la cadena
- 7ExplicaciónUsar el número de asignaciones
- 8DesafíoElegir un nombre con formato de tamaño limitado
- 9ExplicaciónLeer una línea en un espacio de almacenamiento conocido
- 10EjemploSeguir una línea a través de dos lecturas
- 11ConsejoEliminar el final sin eliminar datos
- 12ExplicaciónAnalizar la línea ya leída
- 13DesafíoAceptar una línea que cabe exactamente
- 14DesafíoRechazar un prefijo con un sufijo no válido
- 15RepasoMantener separadas la lectura y la interpretación
3Transferir bytes y definir su significado
12 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónSeparar los bytes de los valores
- 2ExplicaciónElegir qué cuenta el valor de retorno
- 3EjemploSeguir el bloque final corto
- 4Idea claveDistinguir un registro de un bloque de bytes
- 5DesafíoUsar el recuento de la lectura correcta
- 6ExplicaciónNo dejar que el compilador diseñe el formato
- 7EjemploDecodificar un valor con el byte menos significativo primero
- 8EjemploLocalizar un registro a partir de su disposición
- 9DesafíoComprobar la cantidad de datos declarada
- 10ConexiónSeguir la estructura que permite el formato
- 11DesafíoExigir un campo completo antes de decodificar
- 12RepasoHacer que la transferencia y el formato coincidan
4Encontrar un registro y sustituirlo de forma segura
13 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónAcceder directamente a los datos que necesitas
- 2ExplicaciónSeleccionar un punto de referencia
- 3EjemploLocalizar el registro con índice 3 desde cero
- 4ConsejoRestaurar posiciones de texto como valores opacos
- 5DesafíoEncontrar el tercer registro
- 6ExplicaciónConservar el archivo existente
- 7EjemploRestaurar el principio del registro
- 8Idea claveCompletar la sustitución
- 9DesafíoElegir tanto el modo como el desplazamiento
- 10ConexiónReescribir cuando un registro crece
- 11ConsejoMantener recuperable la instalación
- 12DesafíoInterpretar una adición completada
- 13RepasoEl posicionamiento forma parte de una actualización válida
5Reflejar el fallo en los mensajes y la liberación de recursos
12 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónEl éxito incluye el cierre final
- 2ExplicaciónLeer el resultado de cada función
- 3ExplicaciónExplicar por qué se detuvo la entrada
- 4DesafíoRechazar una cabecera truncada
- 5ExplicaciónGuardar el motivo original
- 6EjemploImprimir a partir del código guardado
- 7DesafíoElegir la fuente correcta del mensaje de diagnóstico
- 8ExplicaciónLlevar el control de los flujos abiertos
- 9EjemploPermitir que la liberación de recursos cambie el éxito
- 10DesafíoDecidir la siguiente acción de un script
- 11Idea claveConservar el original después de un cambio de nombre fallido
- 12RepasoDevolver el resultado de la tarea completa
Ejercicios de Entrada y salida de archivos en C
Set 1: Elegir flujos y procesar entradas completas10 Preguntas de Práctica
Pregunta 1 de 10
07 02 00 00. ¿Qué valor representa?Mostrar respuestaOcultar respuesta
07 02 como un par con el byte más significativo primero daría 1794. El orden de los bytes de la propia máquina no cambia la interpretación especificada por el formato de archivo.Pregunta 2 de 10
Mostrar respuestaOcultar respuesta
"rb" abre un archivo existente para entrada binaria y conserva sus bytes almacenados. "r" selecciona el modo texto, que puede transformar los finales de línea y reconocer Ctrl+Z como fin de la entrada en Windows. Los modos de escritura no proporcionan el acceso de lectura solicitado.Pregunta 3 de 10
Mostrar respuestaOcultar respuesta
"w" borraría las entradas anteriores del registro en cuanto la apertura tuviera éxito. El modo "r" conserva los datos, pero no permite escribir la nueva entrada; "w+" permite la entrada y la salida, pero también borra el archivo.Pregunta 4 de 10
fgets lee correctamente la última línea, que contiene "maple" sin salto de línea. ¿Qué deja line[strlen(line) - 1] = '\0';?Mostrar respuestaOcultar respuesta
"maple". fgets no inventa un salto de línea cuando el archivo no lo tiene. Localiza CR o LF con strcspn antes de eliminar el final para que una última línea sin terminación conserve todos sus datos reales.Pregunta 5 de 10
0xFF y contiene más datos. ¿Por qué puede detenerse inmediatamente char c; while ((c = fgetc(fp)) != EOF)?Mostrar respuestaOcultar respuesta
fgetc devuelve el byte como un int no negativo, pero almacenarlo en un char de ocho bits con signo puede convertir 255 en -1. Ese valor puede ser igual al EOF de la implementación en una comparación, deteniendo el bucle antes de los bytes posteriores. Los paréntesis son correctos aquí; el problema lo causa el tipo de destino más estrecho.Pregunta 6 de 10
char tag[14]; int count;, ¿qué llamada lee de forma segura una palabra y un entero en estos objetos?Mostrar respuestaOcultar respuesta
&count, un puntero al destino. El ancho 14 coincide con el tamaño del array, pero permite un carácter de entrada más de los que el array puede contener de forma segura junto con su terminador.Pregunta 7 de 10
Mostrar respuestaOcultar respuesta
stderr mantiene los mensajes de diagnóstico separados de los datos del informe enviados a stdout. Imprimir el mensaje en stdout lo situaría dentro del informe redirigido y podría alterar su formato esperado. La entrada estándar proporciona datos para leer; no es el destino de un mensaje de diagnóstico.Pregunta 8 de 10
fwrite(a, 4, 7, fp) escribe todos los elementos solicitados. ¿Qué devuelve?Mostrar respuestaOcultar respuesta
fwrite devuelve el número de elementos completos escritos, por lo que devuelve 7. La transferencia abarca 28 bytes porque cada elemento tiene tamaño 4, pero ese total de bytes no es el valor de retorno. Los recuentos de bytes y de elementos solo coinciden cuando el tamaño de elemento es 1.Pregunta 9 de 10
Mostrar respuestaOcultar respuesta
SEEK_CUR sumaría esa ubicación absoluta a la posición actual en lugar de seleccionarla.Pregunta 10 de 10
fread(&h, 16, 1, fp). La llamada devuelve 0, ferror(fp) es cero y feof(fp) es distinto de cero. ¿Cómo debe proceder el lector?Mostrar respuestaOcultar respuesta
ferror no hace seguro interpretar sus campos.Set 2: Gestionar límites de texto y disposiciones binarias10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
/work/run y abre "data/items.txt". ¿Qué ruta se resuelve?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
producer | consumer > result.txt de un intérprete de comandos, ¿a dónde va de forma predeterminada la salida de error estándar de producer?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
fread(buf, 1, 600, in). ¿Qué tamaños deben usar las escrituras correctas?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
char name[8]; recibe el resultado de snprintf(name, sizeof name, "part-%d", 2468), que devuelve 9. ¿Qué debe concluir el programa?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
"cedars\nmaplesx". El programa llama repetidamente a fgets(buf, 8, fp). ¿Qué límites de registro debe reconocer?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
"north\nsouth\neast". ¿Cuántas líneas y caracteres de salto de línea contiene?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
uint32_t, pero difieren en el orden de los bytes y el relleno de estructuras. Un programa que escribe un formato vuelca struct { char kind; uint32_t count; } con una llamada a fwrite. ¿Qué cambio de diseño hace que la representación sea independiente de ambas diferencias?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
long p = ftell(fp); y se comprobó. Después de leer más, ¿qué llamada válida vuelve a ella?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
"r+b" se desplaza al byte 40 y lee un registro completo sin llegar a EOF. Se modifica un campo. ¿Qué operación antes de escribir permite el cambio de dirección y selecciona ese mismo registro?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Set 3: Actualizar registros y conservar resultados fiables10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
"x\ny\n" en modo texto. ¿Cuántos bytes se almacenan?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
"64km" a strtol después de poner errno a cero. El valor de retorno es 64, errno sigue siendo 0 y end apunta a k. El campo debe ser un entero completo con caracteres en blanco finales opcionales. ¿Cuál es la decisión correcta?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
fread(b, 16, 1, fp) con un array de 16 bytes. ¿Qué devuelve la segunda llamada y cómo puede usarse su elemento?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
"18 pear 26". Después de una llamada correcta a fscanf(fp, "%d", &n), la misma llamada devuelve 0 dos veces. ¿Por qué no ha llegado a 26?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
"r+b" se desplaza al byte 38 y lee un registro completo sin llegar a EOF. Modifica un campo. ¿Qué operación posterior selecciona el mismo registro y permite escribir la sustitución?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Set 4: Diagnosticar fallos en un proceso completo con archivos10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
"ready" falla aunque las letras visibles coinciden. ¿Qué carácter conservado explica esto?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
totals.txt desde el intérprete de comandos. ¿Qué operador de redirección necesita?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
"a+b", un programa se desplaza al byte 12, lee un registro, cambia su puntuación a 25, vuelve al byte 12 y escribe el registro. Todas las llamadas tienen éxito. ¿Qué puntuaciones tienen después el segundo y el último registro?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
char buf[8], las lecturas devuelven "8642kg\n" y después "1234567"; la siguiente lectura comprobada confirma el fin de archivo normal. strtol encuentra 8642 con kg al final en el primer registro y convierte todos los dígitos del segundo, sin errores de rango. ¿Qué registros son válidos?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
"a\nb". Un programa usa int c; size_t n = 0, lines = 0; while (c = fgetc(in) != EOF) { ++n; if (c == '\n') ++lines; }. ¿Qué totales se obtienen y qué corrección permite contar correctamente las líneas?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
¿Te atascas con una pregunta tuya? Ayuda con Tareas
Términos Clave
A
Acceso aleatorio
Actualización en la misma posición
Almacenamiento en búfer
Análisis de los campos de una línea
Ancho de campo
%23s en la entrada deja espacio para un terminador en un array de caracteres de 24 bytes.C
Cabecera del archivo
clearerr
Copia por bloques
Creación exclusiva
"wx" de C11 protege un informe existente para que no se borre durante la apertura.D
Devolución de un carácter al flujo
ungetc para dejarlo para la siguiente etapa.Directorio de trabajo actual
"data/input.txt".E
Elemento parcial
fread.Entrada estándar
stdin del que un programa recibe normalmente su entrada principal.< values.txt, un filtro lee datos de un archivo mediante stdin en lugar del teclado.Entrecomillado de campos CSV
EOF
int para que un byte real no se confunda con este indicador.ERANGE
errno a cero antes de strtol para que un error de rango antiguo no se confunda con uno nuevo.errno
Especificación de formato
%lf requiere un destino double *, mientras que %f requiere un destino float *.Estado de salida
&& no debe ejecutarse tras una copia de archivo incompleta.F
Fallo de coincidencia
"oak" puede devolver 0 mientras los indicadores de fin de archivo y de error del flujo permanecen desactivados.fclose
EOF.fclose(fp) retorne, incluso con un error, ese flujo no puede volver a usarse.fgetc
int, o devuelve EOF.fgetc haya devuelto correctamente un carácter.fgets
Firma de archivo
Flujo
Flujo de actualización
+ que permite tanto la entrada como la salida bajo las reglas de cambio de dirección."r+b" lo conserva para acceso de lectura y escritura.fopen
NULL.fgetc o fclose.fpos_t
fgetpos y fsetpos para guardar y restaurar toda la información necesaria de posición en el archivo.fputc
EOF si falla.fputc después de transformar el carácter leído.fread
fseek
ftell
long y devuelve -1L si falla; una posición de texto debe tratarse como una ubicación guardada.fwrite
I
Indicador de error
fgets devuelva NULL, ferror puede distinguir un fallo de acceso de una finalización normal.Indicador de fin de archivo
M
Modo binario
"rb" para que la transformación de texto no pueda cambiar sus bytes comprimidos.Modo de adición al final
"a+b" puede seleccionar la entrada, pero no puede redirigir la siguiente escritura.Modo de apertura
fopen qué acceso se permite y si el contenido existente se conserva o se borra."a" conserva las entradas anteriores del registro y sitúa cada nueva escritura al final.Modo texto
N
Número de asignaciones
fscanf solo está completo cuando el número de asignaciones devuelto es 2.O
Orden con el byte más significativo primero
00 00 01 02 en un campo de 32 bits con el byte más significativo primero.Orden con el byte menos significativo primero
02 01 00 00 representan 258 en un campo de 32 bits con el byte menos significativo primero.Orden de los bytes
P
perror
errno en la salida de error estándar, seguidos de un salto de línea.Posición en el archivo
Propiedad de los recursos
Puntero a FILE
FILE * que da a las funciones de biblioteca acceso a un flujo y a su estado almacenado.fp a fgetc después de comprobar que la apertura devolvió un puntero válido a un flujo.Puntero al final de la conversión
strtol señala el primer carácter después del prefijo numérico aceptado por la conversión."86cm", el puntero llega a la c, lo que permite que un campo de número entero rechace el sufijo.R
Redirección
2> puede guardar los mensajes de diagnóstico de un programa en un archivo separado.Relleno de una estructura
char seguido de un int puede ocupar ocho bytes aunque sus tamaños sumen cinco.rename
Ruta de liberación de recursos
S
Salida de error estándar
stderr que se usa para mensajes de diagnóstico independientemente del flujo de resultados normales de un programa.stderr mientras los datos válidos van a stdout.Salida estándar
stdout que se usa para los resultados normales y otras salidas principales de un programa.stdout de un convertidor a la entrada estándar de otro programa.snprintf
strcspn
"\r\n" como conjunto de rechazo permite localizar un final sin eliminar datos finales ordinarios.strerror
errno.Sustitución recuperable
T
Terminador nulo
fgets puede leer.Tipo entero de ancho exacto
uint32_t sigue necesitando una codificación explícita de bytes para que un archivo de intercambio sea portable.Tubería
Sobre esta unidad
Crea tus propios Materiales de Estudio
Convierte tus notas, diapositivas y documentos en Guías de Estudio, Preguntas de Práctica, Flashcards y 6 herramientas más.