C++笔记day11-文件(下)

156 阅读3分钟

printf --- sprintf --- fprintf:

变参函数:参数形参中 有“...”, 最后一个固参通常是格式描述串(包含格式匹配符), 函数的参数个数、类型、顺序由这个固参决定。

printf("hello");
	
printf("%s", "hello");

printf("ret = %d+%d\n", 10, 5);

printf("%d = %d%c%d\n", 10+5, 10, '+', 5);			--> 屏幕


char buf[1024];   //缓冲区  

sprintf(buf, "%d = %d%c%d\n", 10+5, 10, '+', 5);		--> buf 中

FILE * fp = fopen();

fprintf(fp, "%d = %d%c%d\n", 10+5, 10, '+', 5);			--> fp 对应的文件中

scanf --- sscanf --- fscanf

scanf("%d", &m);		键盘 --> m


char str[] = "98";

sscanf(str, "%d", &m);		str --> m


FILE * fp = fopen("r");

fscanf(fp, "%d", &m);		fp指向的文件中 --> m

fprintf()函数:

int fprintf(FILE * stream, const char * format, ...);

fscanf()函数:

int fscanf(FILE * stream, const char * format, ...);

	成功:正确匹配的个数。

	失败: -1

1) 边界溢出。 存储读取的数据空间。在使用之前清空。 memset(buf,0sizeof(buf));

2fscanf函数,每次在调用时都会判断下一次调用是否匹配参数2, 如果不匹配提前结束读文件。(feof(fp) 为真)。

示例

void write_file() //fprintf 文件写函数
{
	FILE* fp = fopen("abc.c", "w");
	if (!fp)
	{
		perror("fopen error");
		return;
	}
	fprintf(fp, "%d%c%d=%d\n", 10, '*', 5, 10 * 5);
	fclose(fp);
}

void read_file() //fscanf 文件读函数
{
	int a, b, c;
	char ch;
	FILE* fp = fopen("abc.c", "r");
	if (!fp)
	{
		perror("fopen error");
		return;
	}
	fscanf(fp, "%d%c%d=%d\n", &a, &ch, &b, &c);
	printf("%d%c%d=%d\n", a, ch, b, c);
	//while (1)  如果要采用循环方式打印的话要注意
	//{
	//	fscanf(fp, "%d\n", &a);
	//	printf("%d\n", a); 打印要放在判断前面,否则会少一位;
	//	if (feof(fp)) 
	//	{
	//		break;
	//	}
	//}
	fclose(fp);
}

int main(void)
{
	write_file();
	read_file();

	system("pause");
	return EXIT_SUCCESS;
}

fgets和fscanf比较

示例

void write_file() //fprintf 文件写函数
{
	FILE* fp = fopen("test01.txt", "w");
	if (!fp)
	{
		perror("fopen error");
		return;
	}
	fprintf(fp, "%d\n", 10);
	fprintf(fp, "%d\n", 6);
	fprintf(fp, "%d\n", 8);
	fclose(fp);
}

void read_file() //fscanf 循环读文件 必须考虑格式是否匹配的问题
//fscanf先打印再判断
{
	int a;
	FILE* fp = fopen("test01.txt", "r");
	if (!fp)
	{
		perror("fopen error");
		return;
	}
	while (1)  
	{
		fscanf(fp, "%d\n", &a);
		printf("%d\n", a); 
		if (feof(fp)) 
		{
			break;
		}
	}
	fclose(fp);
}
void read_file1() //fgets 循环读文件 不会考虑格式是否匹配的问题;
//fgets最好先判断在打印
{
	char buf[1024];
	FILE* fp = fopen("test01.txt", "r");
	if (!fp)
	{
		perror("fopen error");
		return;
	}
	while (1)
	{
		memset(buf, 0, 1024);
		fgets(buf, 1024, fp);
		printf("%s", buf);
		if (feof(fp))
		{
			break;
		}
	}
	fclose(fp);
}

int main(void)
{
	write_file();
	read_file1();
	system("pause");
	return EXIT_SUCCESS;
}

练习:文件版排序

生成随机数,写入文件。将文件内乱序随机数读出,排好序再写回文件。

示例

void write_random() 
{
	FILE* fp = fopen("test02.txt", "w");
	if (!fp) 
	{
		perror("fopen error");
		return;
	}
	srand(time(NULL));//随机数种子;
	
	for (size_t i = 0; i < 10; i++)
	{
		fprintf(fp, "%d\n", rand() % 100);//0-99之间的随机数并写入文件;
	}
	fclose(fp);
}



void BubbleSort(int* src, int len)
{
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = 0; j < len - 1 - i; j++)
		{
			if (src[j] > src[j + 1])
			{
				int temp = src[j];
				src[j] = src[j + 1];
				src[j + 1] = temp;
			}
		}
	}
}

void read_random()
{
	int arr[10];
	int i = 0;
	FILE* fp = fopen("test02.txt", "r");
	if (!fp)
	{
		perror("fopen error");
		return;
	}

	while (1)
	{
		fscanf(fp, "%d\n", &arr[i]);
		i++;
		if (feof(fp))
		{
			break;
		}
	}
	for (size_t i = 0; i < 10; i++)
	{
		printf("%d\n", arr[i]);
	}
	BubbleSort(arr, 10); //冒泡函数进行排序
	fclose(fp);

	FILE* fp1 = fopen("test02.txt", "w"); //重新打开文件清空
	if (!fp1)
	{
		perror("fopen error");
		return;
	}
	srand(time(NULL));//随机数种子;

	for (size_t i = 0; i < 10; i++) //写入排好序的数组
	{
		fprintf(fp1, "%d\n", arr[i]);//把排好序的数组写回文件;
	}
	fclose(fp1);
}

int main(void)
{
	//write_random();
	read_random();
	system("pause");
	return EXIT_SUCCESS;
}

fwrite和fread函数

fgetc --- fputc

fgets --- fputs

fprintf -- fscanf		默认处理文本文件。

fwrite()函数: 既可处理文本文件也可处理二进制文件。

写出数据到文件中。

stu_t stu[4] = { ...... };

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

	参1:待写出的数据的地址

	参2:待写出数据的大小

	参3:写出的个数				-- 参2 x 参3 = 写出数据的总大小。

	参4:文件

	返回值: 成功:永远是 参3 的值。 --- 通常将参21. 将参3传实际写出字节数。

		 失败:0 

示例

typedef struct student 
{
	int age;
	char name[10];
	int num;
} stu;

int main(void)
{
	stu stu1[4] =
	{
		18,"afei",10,
		20,"andyi",20,
		17,"nvnv",15,
		15,"wang",14,
	};

	FILE* fp = fopen("test03.txt", "w");
	if(!fp)
	{
		perror("fopen error");
		return -1;
	}

	int ret = fwrite(&stu1[0], 1, sizeof(stu)*4, fp);
	if (ret == 0) 
	{
		perror("fwrite error");
		return -1;
	}
	printf("%d\n", ret);

	fclose(fp);
	system("pause");
	return EXIT_SUCCESS;
}

fread()函数:

从文件fp中读出数据。

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

	参1:读取到的数据存储的位置

	参2:一次读取的字节数

	参3:读取的次数				-- 参2 x 参3 = 读出数据的总大小

	参4:文件

	返回值: 成功:参数3.	--- 通常将参21. 将参3传欲读出的字节数。				

		 0:读失败 -- 到达文件结尾 -- feof(fp)为真。 

示例

typedef struct student
{
	int age;
	char name[10];
	int num;
} stu;

void write_struct() 
{
		stu stu1[4] =
	{
		18,"afei",10,
		20,"andyi",20,
		17,"nvnv",15,
		15,"wang",14,
	};
		FILE* fp = fopen("test04.txt", "w");
		if (!fp)
		{
			perror("fopen error");
			return -1;
		}

		int ret = fwrite(&stu1[0], 1, sizeof(stu) * 4, fp);
		if (ret == 0)
		{
			perror("fwrite error");
			return -1;
		}

		fclose(fp);
}

void read_file01() //一次读一个元素
{
	FILE* fp = fopen("test04.txt", "r");
	if (!fp)
	{
		perror("fopen error");
		return -1;
	}
	stu buf;

	int ret = fread(&buf, sizeof(stu), 1, fp);

	printf("ret =%d\n", ret);

	printf("age = %d,name =%s,num =%d\n", buf.age, buf.name, buf.num);

	fclose(fp);
}

void read_file() //一次读所有元素
{
	FILE* fp = fopen("test04.txt", "r");
	if (!fp)
	{
		perror("fopen error");
		return -1;
	}
	stu buf[10];//或者开辟堆空间 stu *buf = malloc(sizeof(stu)*1024);
	int i = 0;
	while (1) 
	{
		int ret = fread(&buf[i], sizeof(stu), 1, fp);//一次读一个结构体;
		if (ret ==0)//代替feof来判断到达文件结尾;
		{
			break;
		}
		printf("age = %d,name =%s,num =%d\n", buf[i].age, buf[i].name, buf[i].num);
		i++;
	}
	
	fclose(fp);
}

int main(void)
{
	write_struct();
	read_file();
	system("pause");
	return EXIT_SUCCESS;
}

练习:大文件拷贝

已知一个任意类型的文件,对该文件复制,产生一个相同的新文件。

1. 打开两个文件, 一个“r”, 另一“w”

2. 从r中 fread , fwrite到 w 文件中。

3. 判断到达文件结尾 终止。  

4. 关闭。

注意: 在windows下,打开二进制文件(mp3、mp4、avi、jpg...)时需要使用“b”。如:“rb”、“wb”

示例

void myfile_cp() 
{
	char buf[128] = { 0 };//缓冲区,相当于运水的桶;什么类型没关系,因为是拷贝的字节里的文件
	int ret = 0;

	FILE* rfp = fopen("D:/BaiduNetdiskWorkspace/研究生/课程/编程基础/01-C语言基础/C语言基础-第11天(文件操作下)/1-教学资料/10-午后回顾.avi", "rb");
	if (rfp == NULL) 
	{
		perror("rfp fopen error");
		return;
	}
	FILE* wfp = fopen("test05.avi", "wb");//打开和拷贝二进制文件后面要是rb或wb
	if (wfp == NULL) 
	{
		perror("wfp fopen error");
		return;
	}
	while (1) 
	{
		memset(buf, 0, sizeof(buf));//最好每次运用前把缓冲区清零一下
		ret= fread(buf, 1, sizeof(buf), rfp);
		if (ret == 0) 
		{
			break;
		}
		fwrite(buf, 1, ret, wfp);//这里参3不能用sizeof(buf),而是应该读了多少数据,写出多少数据
	}

	fclose(rfp);
	fclose(wfp);
}

int main(void)
{
	myfile_cp();
	system("pause");
	return EXIT_SUCCESS;
}

随机位置 读:fseek;ftell;rewind

文件读写指针。在一个文件内只有一个。

fseek():

	int fseek(FILE *stream, long offset, int whence);

		参1:文件

		参2:偏移量(矢量: + 向后, - 向前)

		参3:	SEEK_SET:文件开头位置

			SEEK_CUR:当前位置

			SEEK_END:文件结尾位置

	返回值: 成功:0, 失败: -1

ftell():

	获取文件读写指针位置。

	long ftell(FILE *stream);

	返回:从文件当前读写位置到起始位置的偏移量。

	
	借助 ftell(fp) + fseek(fp, 0, SEEK_END); 来获取文件大小。

rewind():

	回卷文件读写指针。 将读写指针移动到起始位置。

	void rewind(FILE *stream);

示例

typedef struct student 
{
	int age;
	char name[10];
	int num;
} stu;

int main(void)
{
	stu stu1[4] =
	{
		18,"afei",10,
		20,"andyi",20,
		17,"nvnv",15,
		15,"wang",14,
	};
	stu s1;
	FILE* fp = fopen("test06.txt", "wb+"); //用二进制的方式写入
	if (!fp) 
	{
		perror("fopen error");
		return -1;
	}
	fwrite(&stu1[0], 1,sizeof(stu1), fp);

	fseek(fp, sizeof(stu), SEEK_SET); //从文件起始位置,向后偏移一个结构体大小

	fread(&s1, 1, sizeof(s1), fp);

	printf("age =%d, name =%s, num =%d", s1.age, s1.name, s1.num);

	int len = ftell(fp);//获取文件当前读写指针的位置,到文件起始位置的偏移量。
	printf("len =%d\n", len);

	rewind(fp);//将文件读写指针回卷到起始;
	fread(&s1, 1, sizeof(s1), fp);

	printf("age =%d, name =%s, num =%d", s1.age, s1.name, s1.num);  //第二次读
	//打印文件大小
	fseek(fp, 0, SEEK_END);
	len = ftell(fp);
	printf("文件大小为:%d\n", len);

	fclose(fp);
	system("pause");
	return EXIT_SUCCESS;
}

Linux和windows文件区别:

1)对于二进制文件操作, Windows 使用“b”, Linux下二进制和文本没区别。

2)windows下,回车 \r, 换行 \n。 \r\n  , Linux下 回车换行\n

3) 对文件指针,先写后读。windows和Linux效果一致。

	      先读后写。Linux无需修改。windows下需要在写操作之前添加 fseek(fp, 0, SEEK_CUR); 来获取文件读写指针,使之生效。

获取文件状态:stat函数

打开文件,对于系统而言,系统资源消耗较大。

int stat(const char *path, struct stat *buf);

	参1: 访问文件的路径

	参2: 文件属性结构体

	返回值: 成功: 0, 失败: -1

示例

int main(void)
{
	struct stat buf;//需要<sys/types.h> <sys/stat.h>头文件
	int ret = stat("test05.txt", &buf);//传出参数:在函数调用结束时,充当函数返回值
	
	printf("文件大小:%d\n", buf.st_size);//不打开文件获取文件大小
	system("pause");
	return EXIT_SUCCESS;
}

删除、重命名文件:remove函数;rename函数

int remove(const char *pathname); 删除文件。

int rename(const char *oldpath, const char *newpath);  重名文件

缓冲区刷新:fflush函数

文件缓冲.png

标准输出-- stdout -- 标准输出缓冲区。   写给屏幕的数据,都是先存缓冲区中,由缓冲区一次性刷新到物理设备(屏幕)

标准输入 -- stdin -- 标准输入缓冲区。	从键盘读取的数据,直接读到 缓冲区中, 由缓冲区给程序提供数据。

预读入、缓输出。

行缓冲:printf(); 遇到\n就会将缓冲区中的数据刷新到物理设备上。

全缓冲:文件。 缓冲区存满, 数据刷新到物理设备上。

无缓冲:perror。 缓冲区中只要有数据,就立即刷新到物理设备。



文件关闭时, 缓冲区会被自动刷新。  隐式回收:关闭文件、刷新缓冲区、释放malloc

手动刷新缓冲区: 实时刷新。

	int fflush(FILE *stream);

		成功:0

		失败:-1
                    

示例

int main(void)
{
	FILE* fp = fopen("test08.txt", "w+");
	if (!fp) 
	{
		perror("fopen error");
		return;
	}
	char m = 0;
	while (1) 
	{
		scanf("%c", &m);
		if (m == ':') 
		{
			break;
		}
		fflush(fp);//手动刷新文件缓冲到物理磁盘上
		fputc(m, fp);
	}
	//当文件关闭时会自动刷新缓冲区;
	fclose(fp);
	system("pause");
	return EXIT_SUCCESS;
}