C语言学习笔记 Day15(C10文件管理--中)

Day15 内容梳理:

目录

C语言学习笔记 Day14(文件管理--上)-CSDN博客

Chapter 10  文件操作

10.2 文件加密、解密

(1)文件加密

(2)文件解密

10.3 文件读取、写入

(1)逐字符读写文件

(2)逐行读写文件

(3)逐数据块读写文件fread()、fwrite()

①读写数字

②读写数组

③读写结构体

10.4 技能应用(文件写入+随机数)

C语言学习笔记 Day16(文件管理--下)-CSDN博客


Chapter 10  文件操作

10.2 文件加密、解密

(1)文件加密

先在D盘手动创建了名为"解密1"的文件,写入了“这是一句话。”

int main(){
	// 文件的加密
	FILE* fp1 = fopen("D:/解密1.txt", "r");
	FILE* fp2 = fopen("D:/加密1.txt", "w");

	char ch;
	while ((ch = fgetc(fp1)) != 0  ) {	//让fp1指针不断指向下一个字符,并判断是否指向了文件终止符0,是则终止while
		//加密
		ch++;
		fputc(ch, fp2);
	}
	fclose(fp1);
	fclose(fp2);
	return 0;
}

结果如下,左侧是原始文件,右侧是加密后的文件

(2)文件解密

继续用上面通过ch++加密过的文件,这次解密文件只需要while循环中让ch--。

int main() {
	// 文件的解密
	FILE* fp1 = fopen("D:/加密1.txt", "r");
	FILE* fp2 = fopen("D:/解密2.txt", "w");

	char ch;
	while ((ch = fgetc(fp1)) != 0) {	//让fp1指针不断指向下一个字符,并判断是否指向了文件终止符0,是则终止while
		//解密
		ch--;
		fputc(ch, fp2);
	}
	fclose(fp1);
	fclose(fp2);
	return 0;
}

结果如下,从“解密1”到“加密1”,再到“解密2”,完成了转换。

10.3 文件读取、写入

(1)逐字符读写文件

读取时:

int main() {
	// (1)字符读取
	FILE* fp = fopen("D:/abc.txt", "r");

	//malloc()是stdlib库的函数,需要在代码最开头加#include<stdlib.h>
	char* p = (char*)malloc(sizeof(char) * 5);	//用malloc函数给指针分配一定存储空间
	
	memset(p, 0, 5);	//是string库的函数,需要在代码最开头加#include<string.h>
	fgets(p, 5, fp);
	printf("%s\n", p);	//打印指针指向的内容
	
	memset(p, 0, 5);
	fgets(p, 5, fp);
	printf("%s\n", p);
	memset(p, 0, 5);
	fgets(p, 5, fp);
	printf("%s\n", p);

	free(p);  //释放p指针所占用的空间资源
	return 0;
}

判断是否到文件结尾的函数:feof(文件指针)

适用范围:文本文件和二进制文件都可

使用方式:没有到文件结尾则返回0,到文件结尾则返回非零值,因此这么用while( ! feof(fp) ){}

写入时:

在字符串中遇到\n不会终止读写,而是换行,但是遇到\0则会终止读写。所以下面分情况探讨了这三种。不过无论是哪种情况,代码整体没有改动。(只有字符串中发生了\n或\0等改动)

情况1:文本没有\n也没有\0的情况:

原文件:                                                             写入后的文件:

情况2:文本有\n的情况,会换行

 原文件:                                                             写入后的文件:

情况3:文本有\0的情况,会只写入\0以前的内容

 原文件:                                                             写入后的文件:

 

(2)逐行读写文件

虽说是逐行,但实际是每次读取指针p大小的内容。比如p大小为2,就一次读2个数据;大小为10就一次读取10个数据。

读取文件:

写入文件:

注意需要导入string库(代码为#include<string.h>)

int main() {
	// 以行读取的形式将内容写入文本文件

	FILE* fp = fopen("D:/code.txt", "w");
	char* p = (char*)malloc(sizeof(char) * 1024);

	while (1) {		//循环条件设为1,持续读取输入的文本
		memset(p, 0, 1024);

		scanf("%[^\n]",p);	//①:吞噬输入字符串中的\n
		getchar();	//追加\n换行

		//判断是否停止输入
		if (!strcmp(p, "comm=exit")) break;		//②

		strcat(p, "\n");	//追加\n换行
		fputs(p, fp);	//将除了comm=exit以外的内容都存入文本文件

	}
	free(p);
	fclose(fp);
	return 0;
}

需要特别注意的有两点。

①代码scanf("%[^\n]",p);的作用:

 是吞噬输入字符串中的\n,也就是不把回车以文本的形式(\n)存入文本文件中。但是考虑到我们仍需要回车来分割字段,所以加了strcat(p,”\n”);来追加回车。

②if( !strcmp(p, “comm=exit”))中加了个“!”:

因为strcmp()的作用是接收两个参数并比较,返回值为0则代表两个值相同。但放回代码看也就是if(0),无法进入条件,所以在strcmp()前加了“!”符号,相当于如果符合条件,就是if(1)。

运行代码后,直接在弹出的调试控制台中输入文本。输入什么都行,我这里输入的是hello world的代码。

得到:

(3)逐数据块读写文件fread()、fwrite()

可以把所有的结构类型都以数据块的形式读取、写入文件

①读写数字
int main() {
	//数据体:写入数字
	FILE* fp = fopen("D:/数据块读写1.txt", "w");

	if (!fp) return -1;	//如果文件打开失败,返回-1

	//(1)存入数字
	int a = 1234;
	fwrite(&a, sizeof(int), 1, fp);	//存储格式:地址、元素大小、元素个数、文件指针

	fclose(fp);
	return 0;
}

写入的时候,因为是转成二进制后以二进制的编码格式显示在文本文件中,所以看不到具体数据。

不过点开文件的属性可以看到,占用4个字节,也就是一个int类型数据的大小

读取的时候,因为操作的是二进制文件,所以读取符号是”rb”而非”r”。

int main() {
	//数据体:读取数字
	FILE* fp = fopen("D:/数据块读写1.txt", "rb");	//在windows中,读取二进制文件是rb而非r

	if (!fp) return -1;	//如果文件打开失败,返回-1

	// 读取数字
	int num;
	fread(&num, sizeof(int), 1, fp);	//存储格式:地址、元素大小、元素个数、文件指针

	printf("%d\n", num); //打印值看看
	fclose(fp);
	return 0;
}

打印结果如下:

②读写数组

代码都和上面相似。

int main() {
	//数据体:写入数组
	FILE* fp = fopen("D:/数据块读写2.txt", "w");

	if (!fp) return -1;	//如果文件打开失败,返回-1

	//(2)存入数组
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	fwrite(arr, sizeof(int), 9, fp);	//存储格式:地址、元素大小、元素个数、文件指针

	fclose(fp);
	return 0;
}

写入的结果:

代码和读取结果如下图:

int main() {
	//数据体:读取数组
	FILE* fp = fopen("D:/数据块读写2.txt", "rb");	//在windows中,读取二进制文件是rb而非r

	if (!fp) return -1;	//如果文件打开失败,返回-1

	// 读取数组
	int arr[9] = {0};	//把元素都先初始化为0
	fread(arr, sizeof(int), 10, fp);	//存储格式:地址、元素大小、元素个数、文件指针

	for (int i = 0; i < 9; i++) {
		printf("%d ", arr[i]); //打印值
	}
	fclose(fp);
	return 0;
}

③读写结构体
typedef struct student {
	char name[20];
	int age;
	int score;
	char addr[50];
}stu;

int main() {
	//数据体:写入结构体
	stu ss[3] = { 
		{"张三",16,90,"单元楼A"},
		{"李四",17,95,"单元楼B"},
		{"王五",15,80,"单元楼C"}
	};
	
	FILE* fp = fopen("D:/数据块读写3.txt", "wb");

	if (!fp) return -1;	//如果文件打开失败,返回-1

	//(3)存入结构体
	for (int i = 0; i < 3; i++) {
		//需要区分的是,sizeof(stu)是单个结构体元素的大小,而sizeof(student)则是整个结构体的大小
		fwrite(&ss[i], sizeof(stu), 1, fp);			//一次写入一条结构体元素。
	}
	fclose(fp);
	return 0;
}

写入结果如下,能显示部分中文,而大部分数据都处于乱码或不可见状态:

读取的代码和结果如下:

10.4 技能应用(文件写入+随机数)

随机生成四则运算题目。

需要用到:文件写入、随机数种子、枚举类(枚举出加减乘除的符号)

分两步进行,一步是随机生成题目,另一步是运算出这些随机式的答案。

第一步,生成四则运算题目

srand()是stdlib库的,time是time库的,需要在代码的最开头写:

#include<stdlib.h>

#include<time.h>

enum operators {
	add, subtract, multiply, divide		//没规定各自代表的数字,默认从0开始,0、1、2、3分别代表四个符号
};

int main() {
	//随机生成四则运算式子
	srand( (int)time(NULL) );	//随机数种子,是stdlib库的

	FILE* fp = fopen("D:/四则运算.txt", "w");
	char* p = (char*)malloc( sizeof(char)* 20 );

	int num1, num2;
	char opt;

	for (int i = 0; i < 10; i++) {
		//随机生成从1到10的数
		num1 = rand()% 10 + 1;
		num2 = rand()% 10 + 1;

		switch (rand() % 4) {	//随机生成一个数,除以4后取余,得到enum类中加减乘除符号对应的值
		case add: opt = '+';
			break;
		case subtract: opt = '-';
			break;
		case multiply: opt = '*';
			break;
		case divide: opt = '/';
			break;
		}
		memset(p, 0, 20);
		// sprintf()是格式化好字符串内容
		sprintf(p, "%d%c%d=\n", num1, opt, num2);	//生成格式:数字1 符号 数字2 = 
		fputs(p, fp);	//把生成的内容写入文本中
	}
	free(p);
	fclose(fp);
	return 0;
}

这里用sprintf()而非printf(),是因为sprintf()可以把内容按格式排列。后续计算四则运算结果所用到的sscanf()也是同理。

结果如下,每运行一次代码,文本内容都会被随机生成的新内容覆盖。

第二步,读取式子并在另一个文本文件中生成式子+答案

首先利用while循环和判断是否到文本末尾的feof()来逐行读取,再利用sscanf()按一定格式取得num1、符号、num2.通过switch判断符号是哪个之后,在对应的case中执行运算,并把结果存储进另一个文件中。

结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值