C语言学习之路(基础篇)—— 文件操作(上)

news2024/11/15 12:24:18

说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!

概述

1) 磁盘文件和设备文件

  • 磁盘文件
    指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上,使用时才调入内存。

  • 设备文件
    在操作系统中把每一个与主机相连的输入、输出设备看作是一个文件,把它们的输入、输出等同于对磁盘文件的读和写。

2) 磁盘文件的分类

计算机的存储在物理上是二进制的,所以物理上所有的磁盘文件本质上都是一样的:以字节为单位进行顺序存储。

在这里插入图片描述
从用户或者操作系统使用的角度(逻辑上)把文件分为:

  • 文本文件: 基于字符编码的文件
  • 二进制文件: 基于值编码的文件

3) 文本文件和二进制文件

3.1 文本文件

  • 基于字符编码,常见编码有ASCIIUNICODE
  • 一般可以使用文本编辑器直接打开
  • 5678的以ASCII存储形式(ASCII码)为:(先将5678转为ASCII码值53、54、55、56,再转为二进制)
    00110101 00110110 00110111 00111000

3.2 二进制文件

  • 基于值编码,自己根据具体应用,指定某个值是什么意思
  • 把内存中的数据按其在内存中的存储形式原样输出到磁盘上
  • 5678的存储形式(二进制码)为:
  • 00010110 00101110

文件的打开和关闭

1) 文件指针

C语言中用一个指针变量指向一个文件,这个指针称为文件指针。

typedef struct
{
	short           level;	//缓冲区"满"或者"空"的程度 
	unsigned        flags;	//文件状态标志 
	char            fd;		//文件描述符
	unsigned char   hold;	//如无缓冲区不读取字符
	short           bsize;	//缓冲区的大小
	unsigned char   *buffer;//数据缓冲区的位置 
	unsigned        ar;	 //指针,当前的指向 
	unsigned        istemp;	//临时文件,指示器
	short           token;	//用于有效性的检查 
}FILE;

FILE是系统使用typedef定义出来的有关文件信息的一种结构体类型,结构中含有文件名、文件状态和文件当前位置等信息。

声明FILE结构体类型的信息包含在头文件"stdio.h"中,一般设置一个指向FILE类型变量的指针变量,然后通过它来引用这些FILE类型变量。通过文件指针就可对它所指的文件进行各种操作。
在这里插入图片描述
C语言中有三个特殊的文件指针由系统默认打开,用户无需定义即可直接使用:

  • stdin: 标准输入,默认为当前终端(键盘),我们使用的scanfgetchar函数默认从此终端获得数据。
  • stdout: 标准输出,默认为当前终端(屏幕),我们使用的printfputs函数默认输出信息到此终端。
  • stderr: 标准出错,默认为当前终端(屏幕),我们使用的perror函数默认输出信息到此终端。

在这里插入图片描述

2) 文件的打开

任何文件使用之前必须打开:

  • 表头文件:#include <stdio.h>
  • 定义函数:FILE * fopen(const char * filename, const char * mode);
  • 功能:打开文件
  • 参数:
    filename:需要打开的文件名,根据需要加上路径
    mode:打开文件的模式设置
  • 返回值:
    成功:文件指针
    失败:NULL

第一个参数的几种形式:

FILE *fp_passwd = NULL;

//相对路径:
//打开当前目录passdw文件:源文件(源程序)所在目录
FILE *fp_passwd = fopen("passwd.txt", "r");

//打开当前目录(test)下passwd.txt文件
fp_passwd = fopen("./test/passwd.txt", "r");

//打开当前目录上一级目录(相对当前目录)passwd.txt文件
fp_passwd = fopen("../passwd.txt", "r");
	
//绝对路径:
//打开C盘test目录下一个叫passwd.txt文件
fp_passwd = fopen("c:/test/passwd.txt","r");

第二个参数的几种形式(打开文件的方式):

打开模式含义
rrb以只读方式打开一个文本文件(不创建文件,若文件不存在则报错)
wwb以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)
aab以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件
r+rb+以可读、可写的方式打开文件(不创建新文件)
w+wb+以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)
a+ab+以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建文件

注意:

  • b是二进制模式的意思,b只是在Windows有效,在Linuxrrb的结果是一样的
  • UnixLinux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾
  • Windows平台下,以 文本 方式打开文件,不加b
  • 当读取文件的时候,系统会将所有的 “\r\n” 转换成 “\n
  • 当写入文件的时候,系统会将 “\n” 转换成 “\r\n” 写入
  • 二进制 方式打开文件,则读写都不会进行这样的转换
  • Unix/Linux平台下, 文本 二进制 模式没有区别,“\r\n” 作为两个字符原样输入输出

示例1: 以只读方式打开一个文本文件(不创建文件,若文件不存在则报错)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>


int main()
{	
	//打开一个文件,成功返回FILE结构体地址,失败返回NULL
	// 返回的文件流指针标识了打开的那个文件
	FILE* fp = fopen("hello.txt", "r"); // 只读,不创建文件,若文件不存在则报错
	if (NULL == fp)
	{
		perror("open error");
		return;
	}


	return 0;
}
输出结果
open error: No such file or directory

示例2: 以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)

FILE* fp = fopen("hello.txt", "w"); // 以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)
if (NULL == fp)
{
	perror("open error");
	return;
}

在这里插入图片描述

编辑"hello.txt"文件,并填写数据后保存,如果再次执行代码,文件内容将被清空

3) 文件的关闭

任何文件在使用后应该关闭:

  • 打开的文件会占用内存资源,如果总是打开不关闭,会消耗很多内存

  • 一个进程同时打开的文件数是有限制的,超过最大同时打开文件数,再次调用fopen打开文件会失败

  • 如果没有明确的调用fclose关闭打开的文件,那么程序在退出的时候,操作系统会统一关闭。

  • 表头文件:#include <stdio.h>

  • 定义函数:int fclose(FILE * stream);

  • 功能:关闭先前fopen()打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源。

  • 参数:
    stream:文件指针

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

FILE* fp = fopen("hello.txt", "w"); // 以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)
if (NULL == fp)
{
	perror("open error");
	return -1;
}
fclose(fp);

文件的顺序读写

1) 按照字符读写文件fgetc、fputc

1.1 写文件

  • 表头文件:#include <stdio.h>
  • 定义函数:int fputc(int ch, FILE * stream);
  • 功能:将ch转换为unsigned char后写入stream指定的文件中
  • 参数:
    ch:需要写入文件的字符
    stream:文件指针
  • 返回值:
    成功:成功写入文件的字符
    失败:返回-1

示例1:清空写入

FILE* fp = fopen("hello.txt", "w"); 
fputc('a', fp);
fclose(fp);

示例2:追加写入

FILE* fp = fopen("hello.txt", "a"); 
fputc('b', fp);
fclose(fp);

示例3:清空循环写入

FILE* fp = fopen("hello.txt", "w"); 
char buf[] = "this is a test for fputc";
int i = 0;
int n = strlen(buf);
for (i = 0; i < n; i++)
{
	//往文件fp写入字符buf[i]
	int ch = fputc(buf[i], fp);
	printf("ch = %c\n", ch);
}
fclose(fp);

在这里插入图片描述

1.2 读文件

  • 表头文件:#include <stdio.h>
  • 定义函数:int fgetc(FILE * stream);
    功能:从stream指定的文件中读取一个字符
    参数:
    stream:文件指针
    返回值:
    成功:返回读取到的字符
    失败:-1

示例:读取文件中的内容

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>


int main()
{
	FILE* fp = fopen("hello.txt", "r");
	char buf[128] = "";
	int i = 0;
	while ((buf[i++] = fgetc(fp)) != -1);
	printf("%s\n", buf);
	return 0;
}

在这里插入图片描述

1.3 文件结尾

C语言中,EOF表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。

#define EOF     (-1)

示例:使用EOF作为结束符,存在的问题

// 写入-1
FILE* fp = fopen("hello.txt", "w");
if (NULL == fp)
{
	perror("open error");
	return -1;
}
char buf[10] = {97,-1,-2,98,99};
int i = 0;
while (buf[i] != 0)
{
	fputc(buf[i], fp);
	i++;
}
fclose(fp);

在这里插入图片描述

int main()
{	
	FILE* fp = fopen("hello.txt", "r");
	char buf[128] = "";
	int i = 0;
	while ((buf[i++] = fgetc(fp)) != EOF);
	printf("%s\n", buf);
	return 0;
}

在这里插入图片描述

当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ANSI C提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。

  • 表头文件:#include <stdio.h>
  • 定义函数:int feof(FILE * stream);
  • 功能:检测是否读取到了文件结尾。判断的是最后一次 读操作的内容 ,不是当前位置内容(上一个内容)。
  • 参数:
    stream:文件指针
  • 返回值:
    0值:已经到文件结尾
    0:没有到文件结尾

示例:使用feof函数来判断文件是否结束

int main()
{	
	FILE* fp = fopen("hello.txt", "r");
	char buf[128] = "";
	int i = 0;
	do
	{
		buf[i++] = fgetc(fp);
	} while (!feof(fp));
	
	printf("%s\n", buf);
	return 0;
}

在这里插入图片描述

1.4 强化训练:实现cp、cat命令

案例1:拷贝文本文件

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>


void copyFile(char srcFileName[128], char dstFileName[128])
{
	// 打开src文件 创建dst文件
	FILE* fpread = fopen(srcFileName, "r");
	FILE* fpwrite = fopen(dstFileName, "w");
	if (NULL == fpread || NULL == fpwrite)
	{
		perror("open error");
		return -1;
	}
	while (1)
	{	
		int ch;
		//  读取src一个字符
		ch = fgetc(fpread);
		if (feof(fpread))
			break;
		// 写入到dst文件
		fputc(ch, fpwrite);

	}
	//关闭
	fclose(fpread);
	fclose(fpwrite);
}

int main()
{	
	char srcFileName[128] = "hello.txt";
	char dstFileName[128] = "hello2.txt";
	copyFile(srcFileName, dstFileName);

	return 0;
}

在这里插入图片描述

案例2:拷贝图片文件

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>


void copyFile(char srcFileName[128], char dstFileName[128])
{
	// 打开src文件 创建dst文件
	FILE* fpread = fopen(srcFileName, "rb"); 
	FILE* fpwrite = fopen(dstFileName, "wb");
	if (NULL == fpread || NULL == fpwrite)
	{
		perror("open error");
		return -1;
	}
	while (1)
	{	
		int ch;
		//  读取src文件
		ch = fgetc(fpread);
		if (feof(fpread))
			break;
		// 写入到dst文件
		fputc(ch, fpwrite);

	}
	//关闭
	fclose(fpread);
	fclose(fpwrite);
}

int main()
{	
	char srcFileName[128] = "csdn_cdtaogang_blog.png";
	char dstFileName[128] = "my_csdn_blog.png"; 
	copyFile(srcFileName, dstFileName);

	return 0;
}

在这里插入图片描述

案例3:实现cat命令,把文件内容输出到终端

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>


int main()
{
	// 打开文件
	FILE *fpread = fopen("04拷贝案例.c", "r");
	if (NULL == fpread)
	{
		perror("open error");
		return -1;
	}
	// 读取文件
	int ch;
	while (1)
	{
		ch = fgetc(fpread);
		if (feof(fpread))
			break;
		fputc(ch, stdout);  //输出到终端
		
	}
	fclose(fpread);

	return 0;
}

在这里插入图片描述

2) 按照行读写文件fgets、fputs

2.1 写文件

  • 表头文件:#include <stdio.h>
  • 定义函数:int fputs(const char * str, FILE * stream);
    功能:将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0' 不写入文件。
  • 参数:
    str:字符串
    stream:文件指针
  • 返回值:
    成功:0
    失败:-1

示例1:将一字符串写入到文件

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>


int main()
{
	// 打开文件
	FILE *fpread = fopen("a.txt", "w");
	if (NULL == fpread)
	{	
		perror("open error");
		return -1;
	}
	// 写入字符串
	char buf[] = "hellocdtaogang";
	fputs(buf, fpread);

	return 0;
}

在这里插入图片描述

示例2:按照行向文件写入数据,遇到\0结束写入,遇到\n就换行

int main()
{
	// 打开文件
	FILE *fpread = fopen("a.txt", "w");
	if (NULL == fpread)
	{	
		perror("open error");
		return -1;
	}
	// 写入字符串,遇到\0就结束
	char buf[] = "hello\0cdtaogang";
	fputs(buf, fpread);

	return 0;
}

在这里插入图片描述

int main()
{
	// 打开文件
	FILE *fpread = fopen("a.txt", "w");
	if (NULL == fpread)
	{	
		perror("open error");
		return -1;
	}
	// 写入字符串,遇到\0就结束,遇到\n就换行
	//char buf[] = "hello\0cdtaogang";
	char buf[] = "hello\ncdtaogang";
	fputs(buf, fpread);

	return 0;
}

在这里插入图片描述

2.2 读文件

  • 表头文件:#include <stdio.h>
  • 定义函数:char * fgets(char * str, int size, FILE * stream);
  • 功能:从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。
  • 参数:
    str:字符串
    size:指定最大读取字符串的长度(size - 1
    stream:文件指针
  • 返回值:
    成功:成功读取的字符串
    读到文件尾或出错: NULL

示例1:从文件中读取一字符串

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>


int main()
{
	// 打开文件
	FILE* fpread = fopen("a.txt", "r");
	if (NULL == fpread)
	{
		perror("open error");
		return -1;
	}
	char buf[1024] = "";
	// 读取文件
	fgets(buf, sizeof(buf), fpread);
	printf("%s", buf);

	fclose(fpread);
	return 0;
}

在这里插入图片描述

示例2:从文件中读取一字符串,遇到\n就结束

在这里插入图片描述

示例3:使用fgetsfputs完成文本文件的拷贝(二进制文件图片读取无法使用,因为字符串二进制文件有很多0,fgets遇到0就读取结束了,同理fputs写入文件也是一样,所以它们只能操作文本文件)

int main()
{
	// 打开a文件 创建b文件
	FILE* fpread = fopen("a.txt", "r");
	FILE* fpwrite = fopen("b.txt", "w");
	if (NULL == fpread || NULL == fpwrite)
	{
		perror("open error");
		return -1;
	}
	char buf[128] = "";
	char* p = NULL;
	while (1)
	{
		//  读取a文件
		p = fgets(buf, sizeof(buf), fpread);
		if (NULL == p)
			break;
		// 写入到b文件
		fputs(buf, fpwrite);
	}
	//关闭
	fclose(fpread);
	fclose(fpwrite);

	return 0;
}

在这里插入图片描述

2.3 强化训练:文件版四则运算

有个文件大小不确定,每行内容都是一个四则运算表达式,还没有算出结果,写一个程序,自动算出其结果后修改文件。
在这里插入图片描述

第一步:随机生成10个四则运算表达式,并写入到文件中。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define CALC_NUM 10  // 要生成四则运算表达式的个数


// 获取10个四则运算表达式并写入到文件中
void write_data()
{	
	// 生成并打开calc.txt文件
	FILE* fp = fopen("calc.txt", "w");
	if (NULL == fp)
	{
		perror("open error");
		return -1;
	}
	// 设置随机种子
	srand(time(NULL));
	// 定义基本运算符数组
	char ysf[] = { '+', '-', '*', '/' };
	int a, b = 0;
	char c = 0;
	// 定义一个buf数组来保存四则运算表达式
	char buf[128] = "";
	for (int i = 0; i < CALC_NUM; i++)
	{
		// 产生随机数1~100
		int a = rand() % 100 + 1;
		int b = rand() % 100 + 1;
		// 随机产生0~3的数
		int c = rand() % 4;  // 0,1,2,3  对应运算符数组下标
		// 组包
		sprintf(buf, "%d%c%d=\n", a, ysf[c], b);
		printf(buf);
		// 写入到calc.txt文件中
		fputs(buf, fp);
	}
	// 关闭文件
	fclose(fp);
}

int main()
{	
	// 调用
	write_data();

	return 0;
}

在这里插入图片描述

第二步:读取calc.txt文件中的内容一行一行的读取,读取一次就进行解包计算结果,再将结果组包到表达式中

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define CALC_NUM 10  // 要生成四则运算表达式的个数
#define FILE_PATH "calc.txt"  // 文件路径


// 封装打开文件方法
FILE* open_file(char* str)
{
	
	FILE* fp = fopen(FILE_PATH, str);
	if (NULL == fp)
	{
		perror("open error");
		return -1;
	}
	return fp;
}

// 封装关闭文件方法
void close_file(FILE* fp)
{
	fclose(fp);
	return;
}

// 获取10个四则运算表达式并写入到文件中
void write_data()
{	
	// 生成并打开calc.txt文件
	FILE* fp = open_file("w");
	// 设置随机种子
	srand(time(NULL));
	// 定义基本运算符数组
	char ysf[] = { '+', '-', '*', '/' };
	int a, b = 0;
	char c = 0;
	// 定义一个buf数组来保存四则运算表达式
	char buf[128] = "";
	for (int i = 0; i < CALC_NUM; i++)
	{
		// 产生随机数1~100
		int a = rand() % 100 + 1;
		int b = rand() % 100 + 1;
		// 随机产生0~3的数
		int c = rand() % 4;  // 0,1,2,3  对应运算符数组下标
		// 组包
		sprintf(buf, "%d%c%d=\n", a, ysf[c], b);
		printf(buf);
		// 写入到calc.txt文件中
		fputs(buf, fp);
	}
	// 关闭文件
	close_file(fp);
}

void read_data()
{	
	// 读取文件
	FILE* fp = open_file("r");
	int a, b = 0;
	char c = 0;
	char* p = NULL;
	char buf[128] = "";
	char new_buf[128] = "";
	int res = 0;
	while (1)
	{
		p = fgets(buf, sizeof(buf), fp); //读一行的数据72*65=\n
		if (NULL == p)
		{
			break;
		}
		// 拆包
		sscanf(buf, "%d%c%d", &a, &c, &b); // 72*65
		// switch判断运算符
		switch (c)
		{
		case '+':
			res = a + b;
			break;
		case '-':
			res = a - b;
			break;
		case '*':
			res = a * b;
			break;
		case '/':
			res = a / b;
			break;
		}
		// 再组包,将计算结果组进去
		sprintf(new_buf, "%d%c%d=%d\n", a, c, b, res); // 72*65=4680\n
		printf("%s", new_buf);
	}
}

int main()
{	
	// 写入
	write_data();
	printf("\n");
	// 读取
	read_data();

	return 0;
}

在这里插入图片描述

第三步:如果直接从第二步去写入结果数据会导致原本的表达式数据被覆盖,比如calc.txt文件13+15=28\n34-21=13\n...在读取第一个\n后写入会直接将\n后面的数据覆盖掉,那么就读取不到后面的数据了,解决方法则是将每行组包数据保存到二维数组中即可

void read_data()
{	
	// 读取文件
	FILE* fp = open_file("r");
	int a, b = 0;
	char c = 0;
	char* p = NULL;
	char buf[128] = "";
	char new_buf[128] = "";
	int res = 0;
	// 定义二维数组保存每行组包结果数据
	char new_buff[10][128] = { 0 };
	int i= 0;
	while (1)
	{
		p = fgets(buf, sizeof(buf), fp); //读一行的数据72*65=\n
		if (NULL == p)
		{
			break;
		}
		// 拆包
		sscanf(buf, "%d%c%d", &a, &c, &b); // 72*65
		// switch判断运算符
		switch (c)
		{
		case '+':
			res = a + b;
			break;
		case '-':
			res = a - b;
			break;
		case '*':
			res = a * b;
			break;
		case '/':
			res = a / b;
			break;
		}
		// 再组包,将计算结果组进去, 
		//sprintf(new_buf[i], "%d%c%d=%d\n", a, c, b, res); // 72*65=4680\n
		//printf("%s", new_buf);
		sprintf(new_buff[i], "%d%c%d=%d\n", a, c, b, res); // 72*65=4680\n
		i++;
		
	}
	// 关闭文件
	close_file(fp);
	// 再次打开calc.txt文件,写入含结果的四则运算表达式
	fp = open_file("w");
	for (int j = 0; j < i; j++)
	{
		fputs(new_buff[j], fp);
	}
	// 关闭文件
	close_file(fp);
}

在这里插入图片描述

也可以将新组包后的结果数据写入到指针数组中,只需要malloc申请空间即可保存组包数据。

char* new_buff[10] = { NULL };
int i= 0;
while (1)
{
	p = fgets(buf, sizeof(buf), fp); //读一行的数据72*65=\n
	if (NULL == p)
	{
		break;
	}
	// 拆包
	sscanf(buf, "%d%c%d", &a, &c, &b); // 72*65
	// switch判断运算符
	switch (c)
	{
	case '+':
		res = a + b;
		break;
	case '-':
		res = a - b;
		break;
	case '*':
		res = a * b;
		break;
	case '/':
		res = a / b;
		break;
	}
	// 再组包,将计算结果组进去, 
	//sprintf(new_buf[i], "%d%c%d=%d\n", a, c, b, res); // 72*65=4680\n
	//printf("%s", new_buf);
	new_buff[i] = (char*)malloc(128);
	sprintf(new_buff[i], "%d%c%d=%d\n", a, c, b, res); // 72*65=4680\n
	i++;
}

3) 按照格式化文件fprintf、fscanf

3.1 写文件

  • 表头文件:#include <stdio.h>
  • 定义函数:int fprintf(FILE * stream, const char * format, ...);
  • 功能:根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符 '\0' 为止。
  • 参数:
    stream:已经打开的文件
    format:字符串格式,用法和printf()一样
  • 返回值:
    成功:实际写入文件的字符个数
    失败:-1

示例:使用fprintf对比sprint组包后fputs写入

printf("%04d:%02d:%02d", year, month, day);
sprintf(buf, "%04d:%02d:%02d", year, month, day)
fprintf(fp, "%04d:%02d:%02d", year, month, day)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>


int main()
{
	int year = 2022;
	int month = 12;
	int day = 2;
	char buf[128] = "";

	FILE* fp = NULL;
	fp = fopen("fprintf.txt", "w");
	if (!fp)
	{
		perror("open error");
		return -1;
	}
	// 组包
	sprintf(buf, "%04d:%02d:%02d", year, month, day);
	// 写入文件
	fputs(buf, fp);

	return 0;
}
	// 组包
	//sprintf(buf, "%04d:%02d:%02d", year, month, day);
	// 写入文件
	//fputs(buf, fp);
	//使用fprintf格式化写入文件
	fprintf(fp, "%04d:%02d:%02d", year, month, day);
	// 关闭文件
	fclose(fp);

在这里插入图片描述

3.2 读文件

  • 表头文件:#include <stdio.h>
  • 定义函数:int fscanf(FILE * stream, const char * format, ...);
  • 功能:从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。
  • 参数:
    stream:已经打开的文件
    format:字符串格式,用法和scanf()一样
  • 返回值:
    成功:参数数目,成功转换的值的个数
    失败: - 1

示例:使用fscanf对文件数据进行拆包

scanf("%d:%d:%d", &year, &month, &day);
sscanf(buf, "%d:%d:%d", &year, &month, &day);
fscanf(fp, "%d:%d:%d", &year, &month, &day);
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>


int main()
{
	FILE* fp = NULL;
	fp = fopen("fprintf.txt", "r");  // 2022:12:02
	if (!fp)
	{
		perror("open error");
		return -1;
	}
	int year = 0, month = 0, day = 0;
	// 使用fscanf进行数据拆包
	fscanf(fp, "%d:%d:%d", &year, &month, &day);
	printf("%d-%d-%d", year, month, day);
	// 关闭文件
	fclose(fp);

	return 0;
}

在这里插入图片描述

3.3 强化训练:文件版排序

10个随机数写入到abc.txt中,然后将abc.txt文件中的随机数进行排序后写入

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define CALC_NUM 10  // 要生成1~3位的整数个数
#define FILE_PATH "abc.txt"  // 文件路径


int main()
{
	// 设置随机种子,并写入数据
	FILE* fp = open_file("w");
	srand(time(NULL));
	for (int i = 0; i < CALC_NUM; i++)
	{
		// 产生随机数1~300
		int num = rand() % 300 + 1;
		// 格式化后写入
		fprintf(fp, "%d\n", num);
	}
	// 关闭文件
	close_file(fp);
	// 读取文件中写入的随机数,并保存到数组中
	int num = 0;
	int nums[10] = { 0 };
	int n = sizeof(nums) / sizeof(nums[0]);
	fp = open_file("r");
	for (int i = 0; i < n; i++)
	{
		// 格式化读取字符串
		fscanf(fp, "%d", &num);
		// 将随机数保存到数组中
		nums[i] = num;
	}
	close_file(fp);
	// 对nums数组元素进行排序
	for (int i = 0; i < n - 1; i++) //比较的轮数
	{	// 因为每次比较的次数都要减1,刚好i每次加1,所以每一轮比较的次数就是n-1-i
		for (int j = 0; j < n - 1 - i; j++) // 每一轮比较的次数
		{
			if (nums[j] > nums[j + 1])  // 交换位置
			{
				int temp = nums[j];
				nums[j] = nums[j + 1];
				nums[j + 1] = temp;
			}
		}
	}
	// 再将排好序的nums数组写入到abc.txt文件
	fp = open_file("w");
	for (int i = 0; i < n; i++)
	{	
		// 将nums每个元素进行组包
		fprintf(fp, "%d\n", nums[i]);
	}
	close_file(fp);
	return 0;
}

在这里插入图片描述

4) 按照块读写文件fread、fwrite

4.1 写文件

  • 表头文件:#include <stdio.h>
  • 定义函数:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
  • 功能:以数据块的方式给文件写入内容
  • 参数:
    ptr:准备写入文件数据的地址
    size: size_tunsigned int类型,此参数指定写入文件内容的块数据大小
    nmemb:写入文件的块数,写入文件数据总大小为:size * nmemb
    stream:已经打开的文件指针
  • 返回值:
    成功:实际成功写入文件数据的块数目,此值和 nmemb 相等
    失败:0

示例:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>


typedef struct _std
{
	int age;
	char name[16];
}STD;

int main()
{	
	int cont = 0;
	STD buf[3] = { {20, "cdtaogang"}, {21, "laoli"}, {22, "laozhao"} };
	FILE* fp = fopen("fwrite.txt", "w");
	// fwrite 第二个参数写1 ,是为了返回值刚好是写入文件的字节数,这也是个技巧
	cont = fwrite(buf, 1, sizeof(buf), fp);
	// cont = fwrite(buf, sizeof(buf), 1, fp);
	// 验证返回值是否等于字节数
	if (cont == sizeof(buf))
	{
		printf("cont == sizeof(buf) == %d", cont); // 60 (int:4 + char name[16]:16)*3
	}

	return 0;
}

在这里插入图片描述

4.2 读文件

  • 表头文件:#include <stdio.h>
  • 定义函数:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • 功能:以数据块的方式从文件中读取内容
  • 参数:
    ptr:存放读取出来数据的内存空间
    size: size_tunsigned int类型,此参数指定读取文件内容的块数据大小
    nmemb:读取文件的块数,读取文件数据总大小为:size * nmemb
    stream:已经打开的文件指针
  • 返回值:
    成功:实际成功读取到内容的块数,如果此值比nmemb小,但大于0,说明读到文件的结尾。
    失败:0

示例1:从结构体数组中,读取一个一个结构体大小

#pragma once
typedef struct _std
{
	int age;
	char name[16];
}STD;
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include "type.h"


int main()
{	
	// 定义结构体数组
	STD buf[3];
	// 全部设置为0
	memset(buf, 0, sizeof(buf));
	FILE* fp = NULL;
	fp = fopen("fwrite.txt", "r"); 
	if (!fp)
	{
		perror("open error");
		return -1;
	}
	int cont = 0;
	// 从结构体数组中,读取一个一个结构体大小
	for (int i = 0; i < 3; i++)
	{
		cont = fread(&buf[i], 1, sizeof(STD), fp);
		printf("cont=%d\n", cont);
		printf("%d %s\n", buf[i].age, buf[i].name);
	}

	return 0;
}
输出结果
cont=20
20 cdtaogang
cont=20
21 laoli
cont=20
22 laozhao

示例2:一次性读完整个结构体数组大小

int main()
{	
	// 定义结构体数组
	STD buf[3];
	// 全部设置为0
	memset(buf, 0, sizeof(buf));
	FILE* fp = NULL;
	fp = fopen("fwrite.txt", "r"); 
	if (!fp)
	{
		perror("open error");
		return -1;
	}
	int cont = 0;
	// 一次性读完整个结构体数组大小
	fread(buf, 1, sizeof(buf), fp);
	for (int i = 0; i < 3; i++)
	{
		printf("%d %s\n", buf[i].age, buf[i].name);
	}

	return 0;
}
输出结果
20 cdtaogang
21 laoli
22 laozhao

4.3 强化训练:大文件拷贝

实现二进制大文件的拷贝

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 1024 * 64



int main()
{
    printf("Start Copy\n");

    // 拷贝的源地址
    char* src_file = "a.mp4";
    // 拷贝的目标地址
    char* dst_file = "b.mp4";

    // 以 可读 + 二进制 方式打开文件
    // r 表示可读打开方式
    // 打开方式后添加 b , 表示以二进制形式打开
    FILE* p_src = fopen(src_file, "rb");
    // 如果打开失败 , 直接返回
    if (p_src == NULL) {
        printf("src_file open failed");
        return 0;
    }

    // 以 可写 + 二进制 方式打开文件
    // w 表示可写打开方式
    // 打开方式后添加 b , 表示以二进制形式打开
    FILE* p_dst = fopen(dst_file, "wb");
    // 如果打开失败 , 直接返回
    if (NULL == p_dst) {
        printf("dst_file open failed");
        return 0;
    }
    // 为缓冲区内存申请堆内存
    char* buffer = malloc(BUFFER_SIZE);

    // 判定文件指针是否指向文件末尾
    // 如果指向末尾 , 说明该文件
    while (!feof(p_src)) {
        // 读取源文件数据到 buffer 缓冲区, 读取 buffer_size 个字节
        // 如果没有那么多字节 , 将读取的字节数返回
        int res = fread(buffer, 1, BUFFER_SIZE, p_src);
        // 将读取到缓冲区中的数据写出到目标文件中
        fwrite(buffer, 1, res, p_dst);
    }

    // 释放缓冲区内存
    free(buffer);
    // 关闭源文件
    fclose(p_src);
    // 关闭目标文件
    fclose(p_dst);

    printf("Copy Success");

	return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/59965.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

亚马逊云科技re:Invent:Serverless是所有构想的核心

12月2日&#xff0c;2022亚马逊云科技re:Invent全球大会上&#xff0c;Amazon.com副总裁兼首席技术官Werner Vogels博士向开发者们展示了另一种可能。在一系列Serverless工具的帮助下&#xff0c;一些代码可以少写&#xff0c;因为未来你可能再也不需要写它们了。这恐怕是自云原…

包装类-Wrapper

包装类的分类 针对八种基本数据类型相应的引用类型-包装类有了类的特点&#xff0c;就可以调用对应的类中的方法 装箱和拆箱 Java是一种面向对象的编程语言&#xff0c;学习Java时就被明确灌输了一个概念&#xff1a;OOP&#xff0c;即面向对象编程。一切皆对象。但是基本…

[附源码]JAVA毕业设计框架的电脑测评系统(系统+LW)

[附源码]JAVA毕业设计框架的电脑测评系统&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技…

Win11右键菜单反应慢有延迟解决方法分享

Win11右键菜单反应慢有延迟解决方法分享。有用户发现电脑鼠标点击右键菜单的时候&#xff0c;会出现一些延迟&#xff0c;导致自己在使用的过程中非常难受。那么这个问题如何自己去进行解决呢&#xff1f;我们一起来看看详细的解决方法分享吧。 解决方法&#xff1a; 注意&…

物联网IoT体系结构及核心技术

物联网&#xff0c;英文名为Internet of things&#xff08;IoT&#xff09;&#xff0c;顾名思义&#xff0c;物联网就是物物相连的互联网。 这有两层意思&#xff1a; 1、物联网的核心和基础仍然是互联网&#xff0c;是在互联网基础上的延伸和扩展的网络&#xff1b; 2、从…

超级详细 的 Redis 安装教程

超级详细 的 Redis 安装教程 Windows 版本的 Redis 是 Microsoft 的开源部门提供的 Redis. 这个版本的 Redis 适合开发人员学习使用&#xff0c;生产环境中使用 Linux 系统上的 Redis, 这里讲解了这两种的安装和下载。按照你们需要的liunx 或window步骤来 就可以了&#xff08;…

智能优化算法:法医调查优化算法 - 附代码

智能优化算法&#xff1a;法医调查优化算法 摘要&#xff1a;法医调查优化算法( Forensic-based investigation algorithm, FBI), 是由 Jui-Sheng Chou 等于2020 年提出的一种群体智能优化算法。其灵感来源于警官调查嫌疑人的过程。 1.法医调查优化算法 警察的大规模案件调查…

Java并发编程—线程详解

文章目录线程简介什么是线程多线程的使用什么时候需要使用多线程&#xff1f;写多少个线程比较合适&#xff1f;线程优先级靠谱的让出CPU的方法&#xff1f;线程的状态线程的状态有哪几种&#xff1f;线程的状态转换Daemon线程启动和终止线程构造线程启动线程理解中断如何安全的…

[附源码]计算机毕业设计基于Springboot的物品交换平台

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

逻辑回归模型和Python代码实现

文章目录逻辑回归原理sigmoid函数优化建模代码实现自编代码sklearn代码代码测试原理测试交叉验证逻辑回归原理 此前介绍的线性回归基本模型和增加了正则项的优化模型都只能用来预测连续值&#xff08;标签值是多少&#xff09;&#xff0c;如果想要应用于分类问题&#xff08;…

回归预测 | MATLAB实现GRU(门控循环单元)多输入单输出

回归预测 | MATLAB实现GRU(门控循环单元)多输入单输出 文章目录 回归预测 | MATLAB实现GRU(门控循环单元)多输入单输出预测效果基本介绍模型结构程序设计参考资料致谢预测效果 基本介绍 GRU神经网络是LSTM神经网络的一种变体,LSTM 神经网 络是在RNN的基础上发展起来的。RNN是一…

Python学习----网络编程

网络&#xff1a;网络就是实现资源共享和信息传递的虚拟平台&#xff0c;我们可以编写基于网络通信的程序。比如socket编程&#xff0c;web开发 Socket编程 Socket是程序之间通信的一个工具&#xff0c;好比显示生活中的电话&#xff0c;你知道了对方的电话号码之后&#xff…

RabbitMQ进阶

可以结合着狂神的RabbitMQ的笔记来进行学习 狂神说RabbitMQ笔记 RabbitMQ高级特性 消息可靠性投递 保证我发出的消息可以到达中间件&#xff0c;避免在传输的过程中发生丢失的情况。 这两个可靠性传输方式分别是负责不同的阶段&#xff0c;confirm是负责保证从生产者到队列…

[附源码]Python计算机毕业设计Django抗疫医疗用品销售平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

2023年江苏省职业院校技能大赛中职赛项规程样题

2023年江苏省职业院校技能大赛中职赛项规程 一、赛项名称 赛项编号&#xff1a;JSZ202335 赛项名称&#xff1a;网络安全 赛项组别&#xff1a;学生组、教师组 赛项归属专业大类&#xff1a;信息技术类 竞赛目的 贯彻落实《国家职业教育改革实施方案》《关于推动现代职业教…

设计模式——观察者模式

动机&#xff08;Motivation&#xff09; 在软件构建过程中&#xff0c;我们需要为某些对象建立一种“通知依赖关系” ——一个对象&#xff08;目标对象&#xff09;的状态发生改变&#xff0c;所有的依赖对象&#xff08;观察者对象&#xff09;都将得到通知。如果这样的依赖…

非零基础自学Golang 2 开发环境 2.1 Go 的安装

非零基础自学Golang 学习文档地址&#xff1a;https://www.topgoer.cn/ 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删【已联系过文档作者】 文章目录非零基础自学Golang2 开发环境2.1 Go 的安装2.1.1 下载地址2.1.2 Go 的安装2.1.3 安装检查2 开发环境…

TypeScript22(Rollup构建TS项目 webpack构建TS项目)

Rollup构建TS项目 安装依赖&#xff1a; 1.全局安装rollup&#xff1a; npm install rollup-g 2.安装TypeScript&#xff1a; npm install typescript -D 3.安装TypeScript 转换器&#xff1a; npm install rollup-plugin-typescript2 -D 4.安装代码压缩插件&#xff1a…

项目:金融行业反欺诈模型

当今以互联网、移动终端等为代表的技术力量正深刻地影响着金融支付市场&#xff0c;信息化、网络化、无线终端等技术的应用&#xff0c;使金融机构特别是银行业的经营发生了天翻地覆的变化&#xff0c;传统的银行柜台和网点业务&#xff0c;正渐渐被电子化交易所替代&#xff0…

[附源码]计算机毕业设计基于SpringBoot的黄河文化科普网站

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…