C语言学习之路(基础篇)—— 文件操作(上)
创始人
2024-04-17 05:36:43
0

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

概述

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
  • 定义函数: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 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

  • 定义函数: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
  • 定义函数: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
  • 定义函数:int fgetc(FILE * stream);
    功能:从stream指定的文件中读取一个字符
    参数:
    stream:文件指针
    返回值:
    成功:返回读取到的字符
    失败:-1

示例:读取文件中的内容

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

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

#define _CRT_SECURE_NO_WARNINGS
#include 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
  • 定义函数: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 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 
#include 
#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~100int 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 
#include 
#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~100int 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=\nif (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\nprintf("%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=\nif (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\ni++;}// 关闭文件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=\nif (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\ni++;
}

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

3.1 写文件

  • 表头文件:#include
  • 定义函数: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 
#include 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
  • 定义函数: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 
#include int main()
{FILE* fp = NULL;fp = fopen("fprintf.txt", "r");  // 2022:12:02if (!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 
#include 
#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~300int 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-ifor (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
  • 定义函数: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 
#include 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
  • 定义函数: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 
#include 
#include "type.h"int main()
{	// 定义结构体数组STD buf[3];// 全部设置为0memset(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];// 全部设置为0memset(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 
#include 
#define BUFFER_SIZE 1024 * 64int 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;
}

在这里插入图片描述

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...