说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!
概述
1) 磁盘文件和设备文件
-
磁盘文件
指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上,使用时才调入内存。 -
设备文件
在操作系统中把每一个与主机相连的输入、输出设备看作是一个文件,把它们的输入、输出等同于对磁盘文件的读和写。
2) 磁盘文件的分类
计算机的存储在物理上是二进制的,所以物理上所有的磁盘文件本质上都是一样的:以字节为单位进行顺序存储。
从用户或者操作系统使用的角度(逻辑上)把文件分为:
- 文本文件: 基于字符编码的文件
- 二进制文件: 基于值编码的文件
3) 文本文件和二进制文件
3.1 文本文件
- 基于字符编码,常见编码有
ASCII
、UNICODE
等 - 一般可以使用文本编辑器直接打开
- 数
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: 标准输入,默认为当前终端(键盘),我们使用的
scanf
、getchar
函数默认从此终端获得数据。 - stdout: 标准输出,默认为当前终端(屏幕),我们使用的
printf
、puts
函数默认输出信息到此终端。 - 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");
第二个参数的几种形式(打开文件的方式):
打开模式 | 含义 |
---|---|
r 或rb | 以只读方式打开一个文本文件(不创建文件,若文件不存在则报错) |
w 或wb | 以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a 或ab | 以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件 |
r+ 或rb+ | 以可读、可写的方式打开文件(不创建新文件) |
w+ 或wb+ | 以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a+ 或ab+ | 以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建文件 |
注意:
b
是二进制模式的意思,b
只是在Windows
有效,在Linux
用r
和rb
的结果是一样的Unix
和Linux
下所有的文本文件行都是\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:使用fgets
和fputs
完成文本文件的拷贝(二进制文件图片读取无法使用,因为字符串二进制文件有很多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_t
为unsigned 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_t
为unsigned 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;
}