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.
Insertar descripción de la imagen aquí
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)
Insertar descripción de la imagen aquí
se puede interpretar usando el método que encontré en la información:
Insertar descripción de la imagen aquí
Visualización de resultados:
Insertar descripción de la imagen aquí

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 imagen de búfer
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:
Insertar descripción de la imagen aquí
después de esperar a que el programa se ejecute durante 10 segundos, obtendrá:
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí

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;//文件指针变量

ARCHIVO
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:

El puntero del archivo apunta a

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:
método abierto
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)

entrada y salida estándar

Lectura y escritura secuencial
escribir salida
Insertar descripción de la imagen aquí
Obtener personajes de la transmisión

Insertar descripción de la imagen aquí
Escribir personajes para transmitir

Insertar descripción de la imagen aquí
Obtener cadena de la secuencia

Insertar descripción de la imagen aquí
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:
resultado de la operación
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:

Insertar descripción de la imagen aquí
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.
Insertar descripción de la imagen aquí
Cambie el contenido en data.txt a:Insertar descripción de la imagen aquí

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:
Insertar descripción de la imagen aquí

Compara un conjunto de funciones:

scanf/fscanf/sscanf

Insertar descripción de la imagen aquí
Leer datos formateados desde stdin

Insertar descripción de la imagen aquí
Leer datos formateados de la secuencia

Insertar descripción de la imagen aquí
Leer datos formateados de una cadena

imprimirf/fprintf/sprintf

Insertar descripción de la imagen aquí
Imprima datos formateados en salida estándar

Insertar descripción de la imagen aquí
Escribir datos formateados para transmitir

Insertar descripción de la imagen aquí
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:
Insertar descripción de la imagen aquí
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;
}

Insertar descripción de la imagen aquí

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:
Insertar descripción de la imagen aquí
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:
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
escribir bloque de datos en la secuencia

Insertar descripción de la imagen aquí
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:
Insertar descripción de la imagen aquí
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:
Insertar descripción de la imagen aquí

Lectura y escritura aleatoria de archivos.

fseek
localiza el puntero del archivo según su posición y desplazamiento.

Insertar descripción de la imagen aquí

Reposicionar el indicador de posición del flujo

ejemplo:

Primero cambie los datos en data.txt como se muestra a continuación:
Insertar descripción de la imagen aquí

/* 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:
Insertar descripción de la imagen aquí

#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:
Insertar descripción de la imagen aquí

#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:
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
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:
Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquí
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:
Insertar descripción de la imagen aquí

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 .

  1. 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.
  2. 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;

Supongo que te gusta

Origin blog.csdn.net/mdjsmg/article/details/131795142
Recomendado
Clasificación