通过代码解释与实现文件的操作

文件的分类:程序文件和数据文件

程序文件包括源程序文件(.c),目标文件(.obj),可执行文件(.exe)

但是不是所有的文件内容都是程序,而是程序运行时读写的数据,如在程序运行时需要读取的文件或者时输出内容的文件。

数据文件的分类

而根据数据的组织形式,数据文件也分为文本文件和二进制文件。

所谓二进制文件,就是内存中的数据不通过任何形式的转换直接输出到外存的文件中,那么这就是二进制文件,对于二进制文件,我们人是无法读懂的,只有在输出到外存时通过ASCII码的形式存储方可读懂。

对于文本文件,其实就是通过ASCII码字符的形式存储的文件。

文件的操作

        我们的程序通常需要与外设进行交互,对于C语言程序而言,文件,画面,键盘等操作都是通过流来操作的。

        流其实是一种抽象的概念,流可以想象成一条有很多字符的河流,当我们对流里存、取数据时,就需要打开流,然后进行操作。

        打开文件(流)——  操作文件(流)——  关闭文件(流)

C语言中默认打开的流

当我们使用scanf和printf函数时,并没有打开流,那为什么还能进行输入和输出?这时因为C语言程序在启动时,默认打开了三个流:

stdin:     标准输入流

stdout:标准输出流

stderr: 标准错误流

文件的打开与关闭

文件规定使用fopen函数来打开,在文件操作完后通过fclose函数来关闭。

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

  形参分别表示打开的文件与打开文件的方式,FILE类型的指针指向需要打开的文件。

int fclose ( FILE * stream );

形参是要关闭的文件,如果关闭成功则返回0,否则返回EOF。

int main() {
	FILE* ft = fopen("text.txt", "w");//以只写的方式打开一个文本文件
	fclose(ft);
	ft = NULL;//需要将指针置空,否则就成为了野指针
}

 文件顺序读写

当我们要对文件中的数据进行存储和读取时,就需要用到顺序独写函数了。

下面是常见的文件顺序读写函数

int fgetc ( FILE * stream );
从流中获取字符(相当于输出)

返回指定流 的内部文件位置指示器当前指向的字符。然后,内部文件位置指示器前进到下一个字符。

如果调用时流位于文件末尾,则函数将返回 EOF 并设置流的文件结束指示器 (feof)。

如果发生读取错误,该函数将返回 EOF 并为流设置错误指示符 (ferror)。

int fputc ( int character, FILE * stream );
将字符写入流(相当于输入)

字符写入并前进位置指示器。

字符被写入的内部位置指示器指示的位置,然后自动前进 1。

 代码使用:通过fputc以此在文件中输入字母x

int main() {
	FILE* ft = fopen("text.txt", "w");
	fputc('x', ft);
	fputc('x', ft);
	fputc('x', ft);
	fclose(ft);
	ft = NULL;
}

当我们开始编译时,当前C语言程序文件下就生成了一个名为“text.txt”的文件,如果源文件下本身就存有该文件名的文件,则不创建。然后在text.txt文件中存入3个x。

int main() {
	FILE* ft = fopen("text.txt", "r");
	int ch = 0;//注意fgetc返回的是一个整形
	while ((ch = fgetc(ft)) != EOF) {
		printf("%c", ch);
	}
    fclose(ft);
	ft = NULL;
}

通过fgetc我们可以输出文件内的数据,每次调用fgetc函数后,文件指示器,也就是光标会自动指向下一个位置 ,可以理解为函数自己实现了指针自加的动作。

单个字符输入输出太过繁琐了,其实对于一整行字符串的输入输出也有相对应的顺序读写函数。

对于整行字符串的读取,可以通过fgets函数实现:

char * fgets ( char * str, int num, FILE * stream );
从 stream 中获取字符串

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

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

终止 null 字符会自动附加到复制到 str 的字符之后。

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

对于整行字符串的写入,我们可以通过fputs函数实现:

int fputs ( const char * str, FILE * stream );
将字符串写入流

将 str 指向的 C 字符串写入

该函数从指定的地址 (str) 开始复制,直到到达终止 null 字符 ('\0')。此终止 null 字符不会复制到流中。

先有写,再有读,如果文件内毫无内容,那还读取什么?

int main() {
	FILE* ft = fopen("text1.txt", "w");
    //与动态内存管理一致,我们应该先判断是否打开了文件,如果打开文件错误就没必要继续运行下去了
	if (ft == NULL) {
		perror("fopen");
		return 1;
	}
	fputs("nihao\n", ft);
	fputs("nihaonihao\n", ft);
	fclose(ft);
	ft = NULL;
}

 在上述代码,我们分别写入了两行代码,nihao和nihaonihao,编译后,在 名为text1.txt的文件内成功写入。

对于文本行的读取,其实和fgetc十分相似,

int main() {
	FILE* pt = fopen("text1.txt", "r");
	if (pt == NULL) {
		return 1;
	}
	char arr[100];
	fgets(arr, 100, pt);
	printf("%s", arr);//读取一行
	//对文件内所有进行读取
	/*while (fgets(arr, 100, pt) != NULL)
	{
		printf("%s", arr);
	}*/
}

对于fgetc,fgets.fputc,fputs这几个顺序读写函数已经介绍完毕

总的来说get是获取文件内数据的,put是往文件加入数据的。

其实在顺序读写函数中,还有fscanf,fprintf,fread,fwrite函数。

当我们看着fscnaf和fprintf函数时,会不由自主的想起scanf和printf,那么他们之间有什么关系吗?

scanf,fscanf,sscanf

让我们先来看一下这几个函数的不同,在cplusplus中,他们的解释如下:

int scanf ( const char * format, ... );
从 stdin 读取格式化数据

从 stdin 读取数据,并根据参数格式将它们存储到其他参数指向的位置。

其他参数应指向已分配的对象,该对象由格式字符串中的相应格式说明符指定的类型。

int fscanf ( FILE * stream, const char * format, ... );
从流中读取格式化数据

中读取数据,并根据参数格式将其存储到其他参数指向的位置。

其他参数应指向已分配的对象,该对象由格式字符串中的相应格式说明符指定的类型。

int sscanf ( const char * s, const char * format, ...);
从字符串中读取格式化数据

从 s 读取数据,并根据参数格式将它们存储到附加参数给出的位置,就像使用 scanf 一样,但从 s 而不是标准输入 (stdin) 读取。

其他参数应指向已分配的对象,该对象由格式字符串中的相应格式说明符指定的类型。

printf,fprintf,sprintf 

int printf ( const char * format, ... );
将格式化数据打印到 stdout

将 format 指向的 C 字符串写入标准输出 (stdout)。如果 format 包含格式说明符(以 % 开头的子序列),则 format 后面的其他参数将被格式化并插入到结果字符串中,以替换它们各自的说明符。

int fprintf ( FILE * stream, const char * format, ... );
将格式化数据写入流

将 format 指向的 C 字符串写入。如果 format 包含格式说明符(以 % 开头的子序列),则 format 后面的其他参数将被格式化并插入到结果字符串中,以替换它们各自的说明符。

在 format 参数之后,该函数至少需要与 format 指定的相同数量的附加参数。

int sprintf ( char * str, const char * format, ... );
将格式化数据写入字符串

使用在 printf 上使用 format 时打印的相同文本编写字符串,但内容不是打印,而是作为 C 字符串存储在 str 指向的缓冲区中。

缓冲区的大小应该足够大,以包含整个结果字符串(有关更安全的版本,请参阅 snprintf)。

终止 null 字符会自动附加到内容之后。

在 format 参数之后,该函数至少需要与 format 所需的相同数量的附加参数。

看着这么多的解释其实挺乱的,还不如直接用代码说话:

int main() {
	//创建一个名为data1.txt的文件,以只写的形式打开
	FILE* ft = fopen("data1.txt", "w");
	if (ft == NULL) {
		perror("fopen");
		return 1;
	}
	int a = 100;
	char b = 'a';
	char name[10] = { "如花" };
	fprintf(ft, "%d%c%s", a, b, name);//将上面三项数据写入文件中
	fclose(ft);
	//以只读的形式打开文件
	ft = fopen("data1.txt", "r");
	//读取文件中a,b两个数据
	fscanf(ft, " %d %c", &a, &b);
	printf("%d %c", a, b);
	fclose(ft);
	ft =NULL; 
	return 0;
}

对于sscanf和sprintf:

int main()
{
	int num = 100;
	float score = 3.14f;
	char name[10] = "小强";

	char str[100] = {0};
    //往str中存入这三个数据
	sprintf(str, "%d %f %s", num, score, name);
	
	printf("字符串的形式:%s\n", str);
	int num1 = 0;
	float score1 = 0;
	char name1[10] = {0};
    将str中的数据提取出来
	sscanf(str, "%d %f %s", &num1, &score1, name1);//从字符串中提取格式化的数据
	printf("格式化的形式:%d %f %s\n", num1, score1, name1);
    //对于两次打印的结果是一致的,sprintf先存,sscanf再取
	return 0;
}

fread与fwrite

fread是可以读取二进制的信息的

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
从流中读取数据块

中读取 count 元素数组,每个元素的大小为 size 字节,并将它们存储在 ptr 指定的内存块中。

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

fwrite可以实现二进制的写 

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
将数据块写入流

将 count 元素数组(每个元素的大小为 size 字节)从 ptr 指向的内存块写入中的当前位置。

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

在内部,该函数将指向的块解释为 ,就好像它是 type 为 的元素数组 ,并按顺序将它们写入,就像为每个字节调用一样。

还是用代码来说话:

int main()
{
	FILE* pf = fopen("data.txt", "wb");//二进制的写
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	
	int arr[] = { 1,2,3,4,5 };

	//二进制的形式写
	fwrite(arr, sizeof(arr[0]), 5, pf);
	fclose(pf);
	//二进制的读
	 pf = fopen("data.txt", "rb");
	int arr2[5] = { 0 };
	//二进制的形式读取
	int i = 0;
	while (fread(arr2 + i, sizeof(arr2[0]), 1, pf))
	{
		printf("%d ", arr2[i]);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值