欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

文件的操作

最编程 2024-10-17 19:38:40
...

程序中的数据都是放在内存中的,

如果要保存这些数据不至于丢失,可以使用文件来保存信息,文件是放在硬盘上

文件的打开关闭

文件打开时,内存创建文件信息区,文件信息区是用来存放文件的相关信息。

这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE文件信息区中是FILE类型的结构体

通过一个FILE的指针来维护这个FILE结构的变量

FILE* pf;

fopen函数和fclose函数

//打开文件

FILE * fopen ( const char * filename, const char * mode );

//关闭文件

int fclose ( FILE * stream );

当使用fopen函数打开文件时,mode可以有多种可能的值

文件的打开和关闭(只写)

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	//操作文件


	//关闭文件
	int rst = fclose(pf);
	pf = NULL;//文件关闭后要对指针赋空
	return 0;
}

文件的打开和关闭(只读)

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	//操作文件


	//关闭文件
	int rst = fclose(pf);
	pf = NULL;//文件关闭后要对指针赋空
	return 0;
}

添加文件后

相对路径

 .表示当前路径(一般默认的就是当前路径)

..表示上一级路径

int main()
{
	//打开文件
	FILE* pf = fopen("..\\data.txt", "r");//打开上一级路径的data.txt文件
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	//操作文件


	//关闭文件
	int rst = fclose(pf);
	pf = NULL;//文件关闭后要对指针赋空
	return 0;
}

添加文件后

当前路径下的

绝对路径

文件的顺序读写

在这之中的所有输出流就包括1.文件输出流 2.标准输出流stdout

fputc函数

int fputc ( int character, FILE * stream );

向文件写入字符

写入成功,返回所写的字符的ASCII值。如果发生写入错误,则返回EOF并设置错误指示器

int main()
{
	//打开文件,只写
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("foprn");
		return;
	}
	//操作文件,向文件写入
	fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);
	fputc('d', pf);

	//关闭文件
	int rst = fclose(pf);
	pf = NULL;
	return 0;
}

写入之前

写入之后

fputc也可以向屏幕写入

int main()
{
	//直接向屏幕上写入
	fputc('a', stdout);//stdout是标准输出流
	fputc('b', stdout);
	fputc('c', stdout);
	fputc('d', stdout);
	return 0;
}

fgetc函数

int fputc(int character, FILE* stream);


函数的功能:

将字符写入流并推进位置指示器。字符被写在流的内部位置指示器指示的位置,然后自动前进一个

函数的返回值:

成功后,返回读取的字符(提升为int值)。

返回类型为int,以容纳表示失败的特殊值EOF:如果位置指示符位于文件末尾,则函数返回EOF并设置流的EOF指示符(feof)。

如果发生其他读取错误,该函数也会返回EOF,但会设置其错误指示器(EOF的本质是-1)

演示fgetc函数

int main()
{
	//打开文件,只读
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("foprn");
		return;
	}
	//操作文件,从文件读出
	int ch = 0;
	ch = fgetc(pf);
	if (ch != EOF)//读取成功
	{
		printf("%c ", ch);
	}
	ch = fgetc(pf);
	if (ch != EOF)//读取成功
	{
		printf("%c ", ch);
	}
	ch = fgetc(pf);
	if (ch != EOF)//读取成功
	{
		printf("%c ", ch);
	}
	ch = fgetc(pf);
	if (ch != EOF)//读取成功
	{
		printf("%c ", ch);
	}
	//关闭文件
	int rst = fclose(pf);
	pf = NULL;
	return 0;
}

正确的代码应该这样写

int main()
{
	//打开文件,只读
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("foprn");
		return;
	}
	//操作文件
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)//只有当读文件的指示符到文件末尾,或者读取文件错误会返回EOF
	{
		printf("%c ", ch);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fputs函数

int fputs ( const char* str,FILE * stream );

将str指向的C字符串写入文件中。函数从指定的地址(str)开始复制,直到到达终止的空字符(“\0”)。此终止空字符不会复制到流中。

请注意,fputs与put的不同之处不仅在于可以指定目标流,而且fputs不会写入额外的字符,而put会自动在末尾添加换行符。

返回值

成功后,返回一个非负值。出现错误时,该函数返回EOF并设置错误指示器(ferror)。

int main()
{
	//打开文件 只写
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//操作文件
	int rst1 = fputs("hello ", pf);
	int rst2 = fputs("world", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

写入前

写入后

fputs在放入一行字符串时遇到\0就停止

int main()
{
	//打开文件 只写
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//操作文件
	int rst1 = fputs("hello\n", pf);
	int rst2 = fputs("worldxxxxx\0xxxxxx", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fputs打印信息到屏幕上


int main()
{
	int rst1 = fputs("hello ", stdout);
	int rst2 = fputs("world", stdout);//这样就把信息打印在屏幕上
	return 0;
}

fgets函数

char* fgets(char* str, int num, FILE* stream);

函数的功能:

从文件中读取字符,并将其作为C字符串存储到str中,直到读取了(num-1)个字符,或者到达换行符或文件末尾,以先发生者为准。

换行符会使fgets停止读取,但函数会将其视为有效字符,并将其包含在复制到str的字符串中。

在复制到str的字符之后,会自动附加一个终止空字符。

请注意,fgets与gets有很大不同:fgets不仅接受流参数,还允许指定str的最大大小,并在字符串中包含任何结束的换行符。

函数的返回值:

成功后,函数返回str。

如果在尝试读取字符时遇到文件末尾,则设置eof指示符(feof)。如果在读取任何字符之前发生这种情况,则返回的指针为空指针(str的内容保持不变)。

如果发生读取错误,则设置错误指示符(ferror)并返回空指针(但str指向的内容可能已更改)。

读到指定位置截止

int main()
{
	//打开文件 只写
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//操作文件
	int rst1 = fputs("hello\nbbbb", pf);
	int rst2 = fputs("worldxxxxx\0xxxxxx", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;

	//打开文件 只读
	FILE* pf1 = fopen("data.txt", "r");
	if (pf1 == NULL)
	{
		perror("fopen");
		return 1;
	}
	//操作文件
	char arr[100] = { 0 };//这个arr表示把文件读到arr这个数组中
	//fgets(arr, 3, pf1);//读入3-1个也就是前2个
	//printf("%s", arr);

	fgets(arr, 4, pf1);
	printf("%s", arr);

	//fgets(arr, 100, pf1);
	//printf("%s", arr);


	fclose(pf1);
	pf1 = NULL;

	return 0;
}

一行有\n那么就读到\n结束

int main()
{
	//打开文件 只写
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//操作文件
	int rst1 = fputs("hello\nbbbb", pf);
	int rst2 = fputs("worldxxxxx\0xxxxxx", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;

	//打开文件 只读
	FILE* pf1 = fopen("data.txt", "r");
	if (pf1 == NULL)
	{
		perror("fopen");
		return 1;
	}
	//操作文件
	char arr[100] = { 0 };//这个arr表示把文件读到arr这个数组中
	//fgets(arr, 3, pf1);//读入3-1个也就是前2个
	//printf("%s", arr);

	fgets(arr, 8, pf1);
	printf("%s", arr);

	//fgets(arr, 100, pf1);
	//printf("%s", arr);


	fclose(pf1);
	pf1 = NULL;

	return 0;
}

无\n读到\0 也截止

fprintf函数,fscanf函数

和printf 函数类似,只是多了个参数,FILE* pf

和scanf 函数类似,只是多了个参数,FILE* pf

向文件写入(fprintf)

struct Stu
{
	char name[10];
	int age;
};

int main()
{
	struct Stu s1 = { "zhangsan",16 };
	//打开文件 只写
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//操作文件,向文件写入
	fprintf(pf, "%s\n%d", s1.name, s1.age);

	fclose(pf);
	pf = NULL;

	return 0;
}

从文件读出(fscanf)


struct Stu
{
	char name[10];
	int age;
};
int main()
{
	struct Stu s = { 0 };
	//打开文件  只读
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//从文件读出
	fscanf(pf, "%s%d", s.name, &(s.age));
	//从文件读出的数据存储在结构体变量s中
	printf("%s-%d", s.name, s.age);
	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

fwrite函数(二进制写入)

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

函数的功能:

从ptr指向的内存块向流中的当前位置写入一个计数元素数组,每个元素的大小为(size)。

文件的位置指示器按写入的总字节数前进。

在内部,该函数将ptr指向的块解释为一个无符号char类型的(size*count)元素数组,并将它们顺序写入流,就像每个字节都调用了fputc一样。

函数的返回值:

返回成功写入的元素总数。

如果此数字与计数参数不同,则写入错误会阻止函数完成。在这种情况下,将为流设置错误指示器(ferror)。

如果size或计数为零,则函数返回零,错误指示器保持不变。size_t是无符号整数类型。

int main()
{
	//打开文件
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	FILE* pf = fopen("data.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//二进制写入
	fwrite(arr, sizeof(arr[0]), 10, pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fread函数(二进制读出

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

从流中读取一个计数元素数组,每个元素的大小为(size),并将其存储在ptr指定的内存块中。

流的位置指示器按读取的总字节数前进。

如果成功,读取的字节总数为(size*count)。

返回值

返回成功读取的元素总数。

如果此数字与计数参数不同,则可能是发生了读取错误,或者在读取时到达了文件末尾。

在这两种情况下,都设置了适当的指示器,可以分别用ferror和feof进行检查。

如果size或count为零,则函数返回零,流状态和ptr指向的内容都保持不变。size_t是一个无符号整数类型。

//二进制的方式从文件读出
//size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

int main()
{
	int arr[10] = { 0 };
	//打开文件
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读出数据
	fread(arr, sizeof(arr[0]), 10, pf);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

sscanf和sprintf函数

int sscanf ( const char * s, const char * format, ...);

int sprintf(char* str, const char* format, ...);

struct S
{
	float f;
	char ch;
	int a;
};

int main()
{
	struct S s = { 3.14f,'w',100 };
	printf("%f-%c-%d", s.f, s.ch, s.a);
	printf("\n");
	char arr[100] = { 0 };
	//把格式化数据转化成字符串
	sprintf(arr, "%f-%c-%d", s.f, s.ch, s.a);
	printf("%s", arr);

	//把字符串转化成格式化数据
	struct S s1 = { 0 };
	sscanf(arr, "%f-%c-%d", &(s1.f), &(s1.ch), &(s1.a));
	printf("\n");
	printf("%f\n", s1.f);
	printf("%c\n", s1.ch);
	printf("%d\n", s1.a);

	return 0;
}

文件的随机读写

fseek,ftell,rewind函数

int fseek ( FILE * stream, long int offset, int origin )

第一个参数是FILE*类型的地址,第二个参数表示的是想要改变的偏移量

第三个参数有三种形式

SEEK_SET  表示文件指针的起始位置(这里的文件指针指的是文件里面的光标)

SEEK_CUR  表示文件指针的当前位置

SEEK_END  表示文件指针的末尾

long int ftell ( FILE * stream );

ftell函数可以告诉我们当前文件指针相较于起始位置的偏移量

rewind函数让文件指针回到起始位置

void rewind ( FILE * stream );

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	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

	ch = fgetc(pf);
	printf("%c\n", ch);//d

	long int num = ftell(pf);
	printf("偏移量为%d\n", num);

	//如果按照顺序接下来要读e
	//但是可以通过fseek函数改变顺序
	
	fseek(pf,-3 , SEEK_CUR);//b
	//fseek(pf, 0, SEEK_SET);
	//fseek(pf, -9, SEEK_END);
	ch = fgetc(pf);
	printf("%c\n", ch);

	rewind(pf);//让指针回到起始位置
	ch = fgetc(pf);
	printf("%c\n", ch);

	return 0;
}

文本文件和二进制文件

数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。(看不懂)

如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件(可以看懂)

如有整数10000,把10000的每一位当作字符,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节)

二进制形式输出,则在磁盘上只占4个字节。

文件读取结束的判定

fgetc    成功返回字符的ASCII值,失败返回EOF

fgets    成功,返回存放数据的起始位置,失败返回空指针

fread   读到几个元素返回值就是几,如果返回值小于实际要读的个数,则可能是发生了读取错误,或者在读取时到达了文件末尾。

模拟实现拷贝

//实现文件的拷贝
int main()
{
	//打开文件 只读
	FILE* pf1 = fopen("data1.txt", "r");
	if (pf1 == NULL)
	{
		perror("fopen");
		return 1;
	}
	//打开文件 只写
	FILE* pf2 = fopen("data2.txt", "w");
	if (pf2 == NULL)
	{
		perror("fopen");
		//文件二打开失败就会退出程序,那么在这之前应该关闭文件一
		fclose(pf1);
		pf1 = NULL;
		return 1;
	}

	//两个文件都打开成功,拷贝文件
	int ch = 0;
	while ((ch=fgetc(pf1)) != EOF)//如果不为EOF就说明读取成功,拷贝
	{
		fputc(ch, pf2);
	}

	//关闭文件
	fclose(pf1);
	pf1 = NULL;
	fclose(pf2);
	pf2 = NULL;

	return 0;
}

文件缓冲器

#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;
}

因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文