Operación de archivos en lenguaje C.
1. ¿Qué es un archivo?
Los archivos en el disco son archivos.
Los archivos de computadora son un tipo de archivo. A diferencia de los soportes de archivos comunes, los archivos de computadora son una colección de información almacenada en una computadora que utiliza el disco duro de la computadora como soporte.
*Por qué utilizar archivos
Utilizando archivos podemos almacenar datos directamente en el disco duro del ordenador, logrando la persistencia de los datos.
——Pero en programación, generalmente hablamos de dos tipos de archivos: archivos de programa y archivos de datos:
archivos de programa
Incluyendo archivos de programa fuente (sufijo .c), archivos de destino (sufijo .obj en entorno Windows) y programas ejecutables (sufijo .exe en entorno Windows).
archivo de datos
El contenido del archivo no es necesariamente el programa, sino los datos leídos y escritos cuando el programa se está ejecutando, como un archivo que necesita leer datos cuando el programa se está ejecutando, o un archivo que genera contenido.
2. Nombre del archivo
Un archivo debe tener un identificador de archivo único para que los usuarios puedan identificarlo y hacer referencia a él.
El nombre del archivo contiene 3 partes: ruta del archivo + tronco del nombre del archivo + sufijo del archivo
. Por ejemplo: c:\code\test.txt
. Por conveniencia, el identificador del archivo a menudo se denomina nombre del archivo.
3. Tipo de archivo
Dependiendo de cómo estén organizados los datos, los archivos de datos se denominan archivos de texto o archivos binarios .
Los datos se almacenan en forma binaria en la memoria y si se envían a la memoria externa sin conversión, es un archivo binario .
Si es necesario almacenarlo en código ASCII en un almacenamiento externo, es necesario convertirlo antes del almacenamiento. Los archivos almacenados en forma de caracteres ASCII son archivos de texto .
¿Cómo se almacenan los datos en la memoria?
Los caracteres siempre se almacenan en formato ASCII y los datos numéricos se pueden almacenar en formato ASCII o binario.
Por ejemplo, si hay un número entero 10000, si se envía al disco en forma de código ASCII, ocupará 5 bytes en el disco (un byte por cada carácter), mientras que si se envía en formato binario, Solo ocupará
4 bytes en el disco.
Prueba de código: (si no comprende esto, puede leer a continuación para comprender el puntero del archivo)
#include <stdio.h>
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
}
Resultado de ejecución: (código binario confuso)
se puede interpretar usando el método que encontré en la información:
Visualización de resultados:
4. Búfer de archivos
El estándar ANSIC utiliza un "sistema de archivos de búfer" para procesar archivos de datos. El llamado sistema de archivos de búfer significa que el sistema crea automáticamente un "búfer de archivos" en la memoria para cada archivo que se utiliza en el programa.
La salida de datos de la memoria al disco se enviará primero al búfer en la memoria y luego se enviará al disco juntos una vez que se llene el búfer.
Si los datos se leen desde el disco a la computadora, los datos se leen desde el archivo del disco y se ingresan en el búfer de memoria (el búfer se llena), y luego los datos se envían desde el búfer al área de datos del programa (variables del programa, etc.) uno por uno. El tamaño del búfer lo determina el sistema de compilación C.
Ejemplo de código:
#include <stdio.h>
#include <windows.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
//注:fflush 在高版本的VS上不能使用了
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);
//注:fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}
Aquí debemos centrarnos en comprender el papel de fflush;
cuando comienza a ejecutarse por primera vez durante 10 segundos, no hay datos en el archivo abierto:
después de esperar a que el programa se ejecute durante 10 segundos, obtendrá:
5. Puntero de archivo (puntos clave)
En el sistema de archivos almacenado en búfer, el concepto clave es el "puntero de tipo de archivo", denominado "puntero de archivo".
Cada archivo utilizado abre un área de información de archivo correspondiente en la memoria para almacenar información relacionada con el archivo (como el nombre del archivo, el estado del archivo y la ubicación actual del archivo, etc.).
Esta información se almacena en una variable de estructura. El tipo de estructura lo declara el sistema y se denomina ARCHIVO.
Por ejemplo, el archivo de encabezado stdio.h proporcionado por el entorno de compilación VS2013 tiene la siguiente declaración de tipo de archivo:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
}; typedef struct _iobuf FILE;
El contenido del tipo FILE de diferentes compiladores de C no es exactamente el mismo, pero sí similar.
Cada vez que se abre un archivo, el sistema creará automáticamente una variable en la estructura del ARCHIVO según la condición del archivo y completará la información que contiene. El usuario no necesita preocuparse por los detalles
.
Generalmente, las variables de esta estructura de ARCHIVO se mantienen a través de un puntero de ARCHIVO, lo que la hace más conveniente de usar.
A continuación podemos crear una variable de puntero de ARCHIVO*:
FILE* pf;//文件指针变量
Defina pf como una variable de puntero que apunta a datos de tipo ARCHIVO.
Puede hacer que pf apunte al área de información del archivo de un determinado archivo (es una variable de estructura).
Se puede acceder al archivo a través de la información en el área de información del archivo. En otras palabras, el archivo asociado
a él se puede encontrar a través de la variable de puntero de archivo . Por ejemplo:
Abrir y cerrar archivos
El archivo debe abrirse antes de leer o escribir y debe cerrarse después de su uso.
Al escribir un programa, cuando se abre un archivo, se devolverá una variable de puntero FILE* que apunta al archivo, lo que equivale a establecer una relación entre el puntero y el archivo.
ANSIC estipula que la función fopen se usa para abrir el archivo y fclose se usa para cerrar el archivo.
FILE * fopen ( const char * filename, const char * mode );
int fclose ( FILE * stream );
Abra de la siguiente manera:
Ejemplo de código:
/* fopen fclose example */
#include <stdio.h>
int main ()
{
FILE * pFile;
pFile = fopen ("myfile.txt","w");
if (pFile!=NULL)
{
fputs ("fopen example",pFile);
fclose (pFile);
}
return 0;
}
Hay dos formas de abrir la dirección de un archivo:
camino relativo
Por ejemplo:
pFile = fopen ("myfile.txt","w");
pFile = fopen (".\\myfile.txt","w");//当前目录
pFile = fopen ("..\\myfile.txt","w");//上一级目录
camino absoluto
Por ejemplo:
pFile = fopen ("C:nser\\Admin\\desktop\\myfile.txt","w");
Lectura y escritura secuencial de archivos (dificultad)
Obtener personajes de la transmisión
Escribir personajes para transmitir
Obtener cadena de la secuencia
Escribir cadena para transmitir
Ejemplo de código:
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
int i = 0;
for (i = 0; i < 26; i++)
{
fputc('a'+i, stdout);
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
Aquí fputc almacenará los datos en el archivo de texto data.txt. Leyenda:
También existe una aplicación simple de fgetc:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(stdin);
printf("%c\n", ch);
ch = fgetc(stdin);
printf("%c\n", ch);
ch = fgetc(stdin);
printf("%c\n", ch);
ch = fgetc(stdin);
printf("%c\n", ch);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
fgetc leerá caracteres del archivo de texto data.txt anterior y, después de cada lectura, fgetc señalará el siguiente carácter y lo leerá en secuencia, como en la siguiente figura:
Ejemplo de código de entradas:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//写文件 - 写一行
fputs("hello bit\n", pf);
fputs("hello xiaobite\n", pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
fputs imprimirá las dos cadenas leídas en la misma línea.
Cambie el contenido en data.txt a:
fgets leerá una cadena de tamaño especificado del segundo ejemplo de código:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//读文件 - 读一行
char arr[10] = {
0 };
fgets(arr, 10, pf);
printf("%s", arr);
fgets(arr, 10, pf);
printf("%s", arr);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
Resultados de la ejecución del código:
Compara un conjunto de funciones:
scanf/fscanf/sscanf
Leer datos formateados desde stdin
Leer datos formateados de la secuencia
Leer datos formateados de una cadena
imprimirf/fprintf/sprintf
Imprima datos formateados en salida estándar
Escribir datos formateados para transmitir
Escribir datos formateados en una cadena.
Ejemplo de código fprintf:
#include<stdio.h>
struct S
{
int a;
float s;
};
int main()
{
FILE* pf = fopen("data.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//写文件
struct S s = {
100, 3.14f };
fprintf(pf, "%d %f", s.a, s.s);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
Resultados de la ejecución del código:
ejemplo de código fscanf:
#include<stdio.h>
struct S
{
int a;
float s;
};
int main()
{
FILE* pf = fopen("data.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//写文件
struct S s = {
0};
fscanf(pf, "%d %f", &(s.a), &(s.s));
fprintf(stdout, "%d %f", s.a, s.s);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
Ejemplo de código sprintf:
#include<stdio.h>
struct S
{
int a;
float s;
char str[10];
};
int main()
{
char arr[30] = {
0 };
struct S s = {
100, 3.14f, "hehe" };
sprintf(arr, "%d %f %s", s.a, s.s, s.str);
printf("%s\n", arr);
return 0;
}
Resultados de la ejecución:
ejemplo de código sscanf:
#include<stdio.h>
struct S
{
int a;
float s;
char str[10];
};
int main()
{
char arr[30] = {
0 };
struct S s = {
100, 3.14f, "hehe" };
struct S tmp = {
0};
sprintf(arr, "%d %f %s", s.a, s.s, s.str);
sscanf(arr, "%d %f %s", &(tmp.a), &(tmp.s), tmp.str);
printf("%d %f %s\n", tmp.a, tmp.s, tmp.str);
return 0;
//}
Resultado de la ejecución del código:
escribir bloque de datos en la secuencia
Leer fragmentos de datos de la transmisión
demostración de código de escritura:
#include<stdio.h>
struct S
{
int a;
float s;
char str[10];
};
int main()
{
struct S s = {
99, 6.18f, "bit" };
FILE* pf = fopen("data.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
fwrite(&s, sizeof(struct S), 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
Resultado de la ejecución del código:
Este es un resultado binario, por lo que la pantalla es confusa; sin embargo, se puede llamar a fread para leer
la demostración del código:
#include<stdio.h>
struct S
{
int a;
float s;
char str[10];
};
int main()
{
struct S s = {
0 };
FILE* pf = fopen("data.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
fread(&s, sizeof(struct S), 1, pf);
printf("%d %f %s\n", s.a, s.s, s.str);
fclose(pf);
pf = NULL;
return 0;
}
resultado de la operación:
Lectura y escritura aleatoria de archivos.
fseek
localiza el puntero del archivo según su posición y desplazamiento.
Reposicionar el indicador de posición del flujo
ejemplo:
Primero cambie los datos en data.txt como se muestra a continuación:
/* fseek example */
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
//定位文件指针到f
fseek(pf, 5, SEEK_SET);//从起始位置开始
int ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
Resultados de la ejecución del código:
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
//定位文件指针到f
fseek(pf, -4, SEEK_END);//从末尾位置开始
int ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
resultado de la operación:
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
//定位文件指针到f
int ch = fgetc(pf);
printf("%c\n", ch);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
ch = fgetc(pf);
printf("%c\n", ch);//c
fseek(pf, 2, SEEK_CUR);
ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
Resultado de la ejecución:
obtenga la posición actual en la secuencia (calcule el desplazamiento)
Ejemplo de código:
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
//定位文件指针到f
int ch = fgetc(pf);
printf("%c\n", ch);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
ch = fgetc(pf);
printf("%c\n", ch);//c
int pos = ftell(pf);
printf("%d\n", pos);
fclose(pf);
pf = NULL;
return 0;
}
resultado de la operación:
Establecer la posición de la corriente al principio (volver a la posición inicial)
Demostración de código:
#include <stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
//定位文件指针到f
int ch = fgetc(pf);
printf("%c\n", ch);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
ch = fgetc(pf);
printf("%c\n", ch);//c
rewind(pf);
ch = fgetc(pf);
printf("%c\n", ch);//a
fclose(pf);
pf = NULL;
return 0;
}
resultado de la operación:
6. Determinación del fin del expediente
feof usado incorrectamente
Recuerde: durante el proceso de lectura del archivo, el valor de retorno de la función feof no se puede utilizar directamente para determinar si el archivo finaliza.
En cambio, se utiliza cuando finaliza la lectura del archivo para determinar si la lectura falló o se encontró el final del archivo .
- Si se completa la lectura del archivo de texto, determine si el valor de retorno es EOF (fgetc) o NULL (fgets),
por ejemplo:
fgetc determina si es EOF,
fgets determina si el valor de retorno es NULL.- El juicio de finalización de lectura del archivo binario determina si el valor de retorno es menor que el número real que se va a leer. Por ejemplo: fread determina si el valor de retorno es menor que el número real que se va a leer.
Uso correcto:
Ejemplo de archivo de texto
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int c; // 注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}
Ejemplo de archivo binario
#include <stdio.h>
enum {
SIZE = 5 };
int main(void)
{
double a[SIZE] = {
1.0,2.0,3.0,4.0,5.0};
double b = 0.0;
size_t ret_code = 0;
FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
fwrite(a, sizeof(*a), SIZE, fp); // 写 double 的数组
fclose(fp);
fp = fopen("test.bin","rb");
// 读 double 的数组
while((ret_code = fread(&b, sizeof(double), 1, fp))>=1)
{
printf("%lf\n",b);
}
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) {
perror("Error reading test.bin");
}
fclose(fp);
fp = NULL;
}
Aquí presentaré otro código para copiar archivos:
//拷贝文件
//拷贝data1.txt 文件,产生一个新的文件data2.txt
#include<stdio.h>
int main()
{
FILE* pfRead = fopen("data1.txt", "r");//打开data1.txt
if (pfRead == NULL)
{
perror("open file for read");
return 1;
}
FILE* pfWrite = fopen("data2.txt", "w");//打开data2.txt
if (pfWrite == NULL)
{
perror("open file for write");
fclose(pfRead);
pfRead = NULL;
return 1;
}
//读写文件
int ch = 0;
while ((ch = fgetc(pfRead)) != EOF)//将data1.txt中的代码依次复制到data2.txt中
{
fputc(ch, pfWrite);
}
//关闭文件
fclose(pfRead);
pfRead = NULL;
fclose(pfWrite);
pfWrite = NULL;
return 0;
}
Resumir
En definitiva, el contenido de este capítulo de operación de archivos es muy complicado, espero que puedas estudiarlo detenidamente y dominarlo lo antes posible;