0 过程总结
1 文件打开
2 文件操作
3 文件关闭
上图源自:https://blog.csdn.net/LG1259156776/article/details/47035583
1 文件指针
FILE *名字;
FILE *fp; // 文件指针
2 打开
2.1 打开文本(=文件)
fp = fopen("路径", "读写法");
其中读写法的引号不能丢!
2.1.1 路径
文件路径层次控制 参见:https://blog.csdn.net/qq_40893824/article/details/105832330
2.1.2 文件名
文件名后缀是 x.txt,对应 我们一般的文件类型
文件名是 x.rar,对应 我们一般的压缩包
如:
直接解压会出现:
说文件损坏了,不是真的损坏,是因为它解压不了!
用 notepad++ 打开就好了:
//
2.1.3 读写法(重点)
下面能写的 可能不能读,要加上 ‘+’!
r、w、a |
/ |
+、b |
/ |
r+、w+、a+ rb、wb、ab rb+、wb+、ab+ |
方法 | 含义 | 若 不存在文件 | 英文单词 |
---|---|---|---|
r | 只读 | 报错 | read |
w | 只写 不能读 写:清空 全部 内容, 从头写 | 新建文件 | write |
a | 只写 不能读 写:在文件 末尾 接着写(追加内容) | 新建文件 | append |
------ | ------------------------------------- | ------------------ | ------------ |
+ | 能读 能写 其他方面保持不变 (就不包含 ‘读写’ 了!) | ||
b | 二进制格式 其他方面保持不变 | binary | |
------ | ------------------------------------- | ------------------ | ------------ |
r+ | 能读 能写 读:从头读 写:从头写 写多少 就覆盖 原文件 同样长度的内容,后面的不覆盖 | 报错 | |
w+ | 能读 能写 读:从头读 写:清空 全部 内容, 从头写 | 新建文件 | |
a+ | 能读 能写 读:从头读 写:在内容 末尾 接着写(追加内容) | 新建文件 | |
------ | ------------------------------------- | ------------------ | ------------ |
rb | 二进制格式 只读 | 报错 | |
wb | 二进制格式 只写 不能读 写:清空 全部 内容, 从头写 | 新建文件 | |
ab | 二进制格式 只写 不能读 写:在文件 末尾 接着写(追加内容) | 新建文件 | |
------ | ------------------------------------- | ------------------ | ------------ |
rb+ | 二进制文件,能读 能写 读:从头读 写:从头写 写多少 就覆盖 原文件 同样长度的内容,后面的不覆盖 | 报错 | |
wb+ | 二进制文件,能读 能写 读:从头读 写:清空 全部 内容, 从头写 | 新建文件 | |
ab+ | 二进制文件,能读 能写 读:从头读 写:在内容 末尾 接着写(追加内容) | 新建文件 |
出错注意
stream != NULL
如提示以上错误 且 程序不能运行终止,说明文件不存在!
方法:
安全性编程
fp = fopen(路径, "r");
if(fp == NULL)
{
fp = fopen(路径, "w+");
}
解析:
1 第一次用 只读 r 打开文件,但文件不存在,此时 fp == NULL成立
那么 if 执行条件成立,用 读写 w+ 方式打开文件,可读可写
2 第二次用 r 打开文件,文件存在,此时 fp == NULL 不成立
那么文件就只能读了,不能重写,此时之前过程中文件内容 不会被清除
测试代码
https://gitee.com/raboy/data_structure.git
2.2 创建文件夹
2.2.1 需要的头文件:
#include <io.h>
#include <direct.h>
遇到 #include<bits/stdc++.h> 都没包含的库 → #include <direct.h>
2.2.2 函数使用解析
_access 和 _mkdir
(1) _access
来源:https://www.jb51.net/article/169245.htm
(1).1 使用形式
int access(路径, int mode);
(1).2 功能
_access 会返回数 0 或 -1,可判断 指定方式是否成立
0 表示 指定方式 有效 ( 或成立 )
-1 表示 指定方式 无效 ( 或不成立 )
mode | 指定方式 |
---|---|
0 | 是否存在 |
4 | 是否可读 |
2 | 是否可写 |
6 | 是否可读可写 |
其中别处写的 mode = 4 表示是否仅读,我认为这样表述不好
因为根据代码运行的情况,创建的一个文本文件,它在以上 4 种方式下均成立
一个 “ 可读可写 ” 的文件,说它仅读,就不太准确了
(2) _mkdir
来源:https://www.cnblogs.com/coolbear/archive/2013/05/09/3068734.html
(2).1 使用形式:
_mkdir(路径);
例如:
路径 path=“d:/A/B/C”,且文件夹 A、B、C 均不存在,若想创建文件夹 C,需 先创建 文件夹 A、B:
_mkdir("d:/A");
_mkdir("d:/A/B");
_mkdir("d:/A/B/C");
(2).2 功能
仅可以创建文件夹,而 不能创建文件!
2.2.3 代码
(1) 最简单的代码
#include<bits/stdc++.h>
//#include <io.h>
#include <direct.h>
using namespace std;
int main()
{
char name[]="./asd";
if(_access(name, 0) == -1)
{
_mkdir(name);
cout << "文件夹 "<< name + 2<< " 创建成功!" <<endl <<endl;
}
system("pause");
return 0;
}
之前: |
![]() |
之后: |
![]() |
(2) _access、_mkdir 函数测试代码
#include<bits/stdc++.h>
//#include <io.h>
#include <direct.h>
using namespace std;
int main()
{
char name[]="./asd/2.txt";
FILE *fp;
// int _access(const char *pathname, int mode);
// 返回 0:指定有效; 1:指定无效
//1 mode 4种情况
// mode = 0 文件 或 目录是否存在
if(_access(name, 0) == -1)
{
cout << "文件夹 "<< name + 2<< " 不存在!" <<endl <<endl;
_mkdir("asd");
fp = fopen(name, "w");
if(fp == NULL)
{
cout<< "can not open the "<< name + 2<< " !"<< endl<< endl;
exit(0);
}
fclose(fp);
cout << "文件夹 "<< name + 2<< " 创建成功!" <<endl <<endl;
}
//2 mode = 4 文件 或 目录是否 可读
if(_access(name, 4) == 0)
cout << "文件夹 "<< name + 2<< " 权限:可读!" <<endl <<endl;
else
cout << "文件夹 "<< name + 2<< " 权限:不可读!" <<endl <<endl;
//3 mode = 2 文件 或 目录是否仅 可写
if(_access(name, 2) == 0)
cout << "文件夹 "<< name + 2<< " 权限:可写" <<endl <<endl;
else
cout << "文件夹 "<< name + 2<< " 权限:不可写!" <<endl <<endl;
//4 mode = 6 文件 或 目录是否 可读可写
if(_access(name, 6) == 0)
cout << "文件夹 "<< name + 2<< " 可读可写!" <<endl <<endl;
else
cout << "文件夹 "<< name + 2<< " 权限:不可读写!" <<endl <<endl;
system("pause");
return 0;
}
之前![]() |
运行后:![]() |
效果:![]() ![]() |
3 字符读写
无规律数据 | 以字符方式读写 | fgetc() file get char | 读文件内容 |
fputc() file put char | 写内容进文件 EOF是文件末尾 | ||
以字符串方式读写 | fgets() file get string | 读文件字符串内容 | |
fputs() file put string | 写字符串内容进文件 | ||
有规律数据 | 以非字节方式读写 格式控制方便! | fscanf() | 读文件内容 |
fprintf() | 写内容进文件 feof(fp)是文件末尾 | ||
以字节方式读写 格式最好的char型,不便于信息物理对齐 | fread() | 读文件字符串内容 | |
fwrite() | 写字符串内容进文件 文件末尾 |
形式 | 作用 |
---|---|
ftell(fp) | 检测 fp 移动了多少字节 |
feof(fp) | 判断 fp 是否到达文件末尾 |
-------- | -------------- |
rewind(fp) | fp 回到文件开头 |
fseek(fp, long size, mode) size: 正数:向前移动 负数:向后移动 数字后要加 L!如:0L mode: SEEK_SET = 0,文件开头 SEEK_CUR = 1,当前位置 SEEK_END = 2,文件末尾 | 在 mode 指定处 指针移动 size 的绝对值 个字节,方向由 正负号决定 |
约定
下面 2.txt事先均不存在!
3.0 知识点
- 文件指针会自动移动
- 文件末尾是EOF
- 下面的对象都是文件
写文件是 fputc(ch, fp)、fputs(str, fp)、fprintf(fp, “%d\n”, i)、fwrite(temp, 3, sizeof(Student), fp);
读文件是 fgetc(fp)、fgets(buff, 1024, fp)、fscanf(fp, “%d”, &i)、fread(temp, 3, sizeof(Student), fp); - fputc(字符ch, 文件指针); // 写:字符ch写进文件
- 字符ch = fgetc(文件指针); // 读:读文件内容,赋给字符ch
- fputs(str, 文件指针); // 写:字符串str写进文件
- fgets(buff, 长度, 文件指针); // 读:读文件内容,赋给buff,长度是buff的容量
- fputs() 与 fgets() 是把真正的字符串写进文件,不包含最后的’\0’,若要在文件中换行,fputs("\n", fp); 即可
- fprint()、fscanf()的用法,和printf()、scanf()相比,括号内部参数的第一个参数是文件指针 或 stdout、stdin,其他不变!stdout 在屏幕显示输出,stdin 在屏幕中读取
fprintf(fp, “%d %f”, a, b); 和 fscanf(fp, “%d%d”, &a, &b);
fprintf(stdout
, “%d %f”, a, b); 和 fscanf(stdin
, “%d%d”, &a, &b);
fscanf() 不能读空格! - fwrite(地址, 个数, 字节, fp);
fread(地址, 个数, 字节, fp);
字节 和 个数 顺序可以反过来 - freopen(path, “r”, stdin); 输入信息从 path 路径的文件中获取,而不是键盘
freopen(path, “w”, stdout); 输出 向 path 路径的文件中写入,而不是屏幕
3.1 字符 读写文件
- fputc(字符ch, 文件指针); // 写:字符ch写进文件
- 字符ch = fgetc(文件指针); //读:读文件内容,赋给字符ch
写文件思路
1 打开文件 w+
2 字符一个一个用fputc()写,文件指针自动移动
读文件思路
1 打开文件 r
2 字符一个一个用fgetc()读,,文件指针自动移动
// 写:w+ fputc()
// 读:r fgetc()
#include<bits/stdc++.h>
using namespace std;
int main()
{
FILE *fp1, *fp2; // 文件指针
/
//写文件: 1打开 2字符一个一个用fputc()写,文件指针自动移动
char *name1 = ".\\2.txt";
int p1 = 2; // name1 + p1 指向文件名1的开头
int i;
fp1 = fopen(name1, "r");
if(fp1 == NULL)
{
cout << "can not open the " << name1 + p1 << "!"<< endl << endl;
fp1 = fopen(name1, "w+");
}
else
cout << name1 + p1 << " has been opened!"<< endl << endl;
char *str = "hello!";
cout << "开始写文件:" << endl;
cout << "str = " << str << endl;
for(i = 0; i < strlen(str) + 1; i++)
fputc(str[i], fp1);
fclose(fp1);
cout << "结束!" << endl << endl;
//
//读文件:1打开 2字符一个一个用fgetc()读,文件指针自动移动
cout << "开始读文件:" << endl;
fp2 = fopen(name1, "r");
if(fp2 == NULL)
{
cout << "can not open the " << name1 + p1 << "!"<< endl << endl;
exit(0);
}
else
cout << name1 + p1 << " has been opened!"<< endl;
char ch = fgetc(fp2);
while(ch != EOF) // EOF是文件末尾
{
putchar(ch);
ch = fgetc(fp2);
}
cout << endl;
fclose(fp2);
cout << "结束!" << endl << endl;
system("pause");
return 0;
}
![](https://img-blog.csdnimg.cn/20200430185444663.png)
![](https://img-blog.csdnimg.cn/20200430185522429.png)
3.2 字符串 读写文件
- fputs(str, 文件指针); // 写:字符串str写进文件
- fgets(buff, 长度, 文件指针); // 读:读文件内容,赋给buff,长度是buff的容量
读到’\n’时会停止读字符,’\n’也读到了,继续下次fgets()
#include<bits/stdc++.h>
using namespace std;
int main()
{
FILE *fp1, *fp2;
//写文件:
char *name1 = "./2.txt";
char *str = "hello!";
fp1 = fopen(name1, "w+");
if(fp1 == NULL)
{
cout << "error arise!" << endl << endl;
exit(0);
}
else
{
cout << "开始写文件:" << endl;
fputs(str, fp1) ;
fclose(fp1);
cout << "完成!" << endl << endl;
}
//读文件:
fp2 = fopen(name1, "r");
if(fp1 == NULL)
{
cout << "error arise!" << endl << endl;
exit(0);
}
else
{
cout << "开始读文件:" << endl;
char buff[1024];
fgets(buff, 1024, fp2);
puts(buff);
fclose(fp2);
cout << "完成!" << endl << endl;
}
system("pause");
return 0;
}
![](https://img-blog.csdnimg.cn/20200430205004183.png)
![](https://img-blog.csdnimg.cn/20200430205106762.png)
3.2.1 cmd命令复制文件
3.2.1.1 基础
cmd使用:https://blog.csdn.net/qq_40893824/article/details/105877687
路径跳转:https://blog.csdn.net/qq_40893824/article/details/103983920
原文件1.txt的内容 复制到 目标文件 2.txt
其中获取原文件1.txt内容字符的的ch可以是 char 类型,也可以是 int 类型!不影响效果
代码:
#include<bits/stdc++.h>
using namespace std;
int main(int argv, char *argc[])
{
if(argv != 3)
{
cout << "输入有误!" << endl;
system("pause");
return 0;
}
else
{
cout << "覆盖 " << argc[2] << " 吗?(Y / N)"<< endl;
char key = getchar();
if(key == 'y' || key == 'Y')
{
FILE *fp1, *fp2;
fp1 = fopen(argc[1], "r");
fp2 = fopen(argc[2], "w");
char ch = fgetc(fp1);
while(ch != EOF)
{
fputc(ch, fp2);
ch = fgetc(fp1);
}
fclose(fp1);
fclose(fp2);
cout << "已复制 1个文件。" << endl << endl;
}
else
cout << "已复制 0个文件。" << endl << endl;
}
system("pause");
return 0;
}
原来:
、
现在的效果:
3.3 格式化读写
3.3.1 fprintf()、fscanf()
fprint()、fscanf()的用法,和printf()、scanf()相比,括号内部参数的第一个参数是文件指针 或 stdout、stdin,其他不变!stdout 在屏幕显示输出,stdin 在屏幕中读取
fprintf(fp, “%d %f”, a, b); 和 fscanf(fp, “%d%d”, &a, &b);
fprintf(stdout
, “%d %f”, a, b); 和 fscanf(stdin
, “%d%d”, &a, &b);
#include<bits/stdc++.h>
#define num 100
using namespace std;
int main()
{
FILE *fp1;
char str[num] = "qwe";
int i;
i = 12;
cout << "str = " << str << endl;
cout << "i = " << i << endl << endl;
cout << "运行 fprintf(stdout, \"%s\\n%d\\n\\n\", str, i); :" << endl;
fprintf(stdout, "%s\n%d\n\n", str, i);
cout << "运行 fscanf(stdin, \"%d\", &i); :" << endl;
fscanf(stdin, "%d", &i);
printf("i = %d\n\n", i);
system("pause");
return 0;
}
![](https://img-blog.csdnimg.cn/20200513183544101.png)
3.3.2 fwite()、fread()
将结构化的信息写入文件,或从文件中读取
fwrite(地址, 个数, 字节, fp);
fread(地址, 个数, 字节, fp);
或
fwrite(地址, 字节, 个数, fp);
fread(地址, 字节, 个数, fp);
字节 和 个数 顺序可以反过来
注意:
1 在生成结构化信息前,一定要 初始化 结构体对象!否则在文件中会出现乱码!
2 结构化信息 最好 是 char 或 char *( = char a[]型) 的,其他形式也会乱码…
在文件中是文字可读的 字符形式,而不是 人类不好读的 2 进制
缺点:
不能控制在文件中的对齐方式,只能将结构化信息写入文件,不能把换行符写入。若信息超过 1 行的长度,也不换行,但会在下 1 行显示,本质没有换行。
#include<bits/stdc++.h>
using namespace std;
typedef struct Student
{
char name[10];
char sex[3];
char age[3];
}Student;
int main()
{
// 写文件:
FILE *fp;
char path[] = "./text.txt";
fp = fopen(path, "w");
if(fp == NULL)
{
cout << "can not open the " << path + 2 << "!" << endl << endl;
exit(0);
}
else
{
Student temp[6]={0};
strcpy(temp[0].name,"张三");
strcpy(temp[0].sex,"男");
strcpy(temp[0].age,"12");
// temp[0].age = 12;
strcpy(temp[1].name,"李四");
strcpy(temp[1].sex,"女");
strcpy(temp[1].age,"15");
// temp[1].age = 12;
strcpy(temp[2].name,"王五");
strcpy(temp[2].sex,"男");
strcpy(temp[2].age,"22");
// temp[2].age = 12;
strcpy(temp[3].name,"王五");
strcpy(temp[3].sex,"男");
strcpy(temp[3].age,"22");
strcpy(temp[4].name,"王五");
strcpy(temp[4].sex,"男");
strcpy(temp[4].age,"22");
strcpy(temp[5].name,"王五");
strcpy(temp[5].sex,"男");
strcpy(temp[5].age,"22");
fwrite(temp, sizeof(Student), 6, fp);
cout << "文件写入成功!" << endl << endl;
}
fclose(fp);
// 读文件:
FILE *fp2;
int i;
fp2 = fopen(path, "r");
if(fp2 == NULL)
{
cout << "can not open the " << path + 2 << "!" << endl << endl;
exit(0);
}
else
{
Student temp2[3];
fread(temp2, 3, sizeof(Student), fp2);
for(i = 0; i < 3; i++)
printf("%s\t%s\t%s\n", temp2[i].name, temp2[i].sex, temp2[i].age);
}
fclose(fp2);
system("pause");
return 0;
}
![](https://img-blog.csdnimg.cn/20200514134009909.png)
3.4 学生信息-文件读写
3.4.1 思路
1 定义 单个 学生信息的结构体(链式 或 顺序)
2 定义 学生群体 的结构体
3 初始化 学生群体 的结构体,即 链表指针为 NULL,长度为 0
4 创建 学生群体 的结构体,学生信息是随机生成的(其中随机生成汉字和数字(整 + 浮) 可参见:https://blog.csdn.net/qq_40893824/article/details/105907220
)
顺序:
单个 学生 信息的结构体
typedef struct
{
char name[num];
int age;
char sex[2];
}Student;
学生群体的结构体
typedef struct
{
Student *sqlist;
int length;
}Stu;
3.4.2 代码
见:https://blog.csdn.net/qq_40893824/article/details/106084316
3.5 文件指针移动
形式 | 作用 |
---|---|
ftell(fp) | 检测 fp 移动了多少字节 |
feof(fp) | 判断 fp 是否到达文件末尾 |
-------- | -------------- |
rewind(fp) | fp 回到文件开头 |
fseek(fp, long size, mode) size: 正数:向前移动 负数:向后移动 数字后要加 L!如:0L mode: SEEK_SET = 0,文件开头 SEEK_CUR = 1,当前位置 SEEK_END = 2,文件末尾 | 在 mode 指定处 指针移动 size 的绝对值 个字节,方向由 正负号决定 |
指针移动到 文件开始位置:
rewind(fp) = fseek(fp, 0L, SEEK_SET); = fseek(fp, 0L, 0);
3.5.1 简单实例
新建学生信息,读取文件,此时,文件指针在文件末尾
再将指针 fp 移动到第 2 个学生信息那里,读取第 2 个学生的信息
代码
#include<bits/stdc++.h>
using namespace std;
typedef struct Student
{
char name[10];
char sex[3];
char age[3];
}Student;
int main()
{
// 写文件:
FILE *fp;
char path[] = "./text.txt";
fp = fopen(path, "w");
if(fp == NULL)
{
cout << "can not open the " << path + 2 << "!" << endl << endl;
exit(0);
}
else
{
Student temp[3]={0};
strcpy(temp[0].name,"张三");
strcpy(temp[0].sex,"男");
strcpy(temp[0].age,"12");
strcpy(temp[1].name,"李四");
strcpy(temp[1].sex,"女");
strcpy(temp[1].age,"15");
strcpy(temp[2].name,"王五");
strcpy(temp[2].sex,"男");
strcpy(temp[2].age,"22");
fwrite(temp, 3, sizeof(Student), fp);
cout << "文件写入成功!" << endl << endl;
}
fclose(fp);
// 读文件:
FILE *fp2;
int i;
fp2 = fopen(path, "r");
if(fp2 == NULL)
{
cout << "can not open the " << path + 2 << "!" << endl << endl;
exit(0);
}
else
{
Student temp2[3];
fread(temp2, 3, sizeof(Student), fp2);
for(i = 0; i < 3; i++)
printf("%s\t%s\t%s\n", temp2[i].name, temp2[i].sex, temp2[i].age);
cout << endl << "测试:" << endl;
// fseek(fp2, sizeof(Student), SEEK_SET);
fseek(fp2, sizeof(Student), 0);
Student t;
fread(&t, sizeof(Student), 1, fp2);
printf("%s\t%s\t%s\n", t.name, t.sex, t.age);
}
fclose(fp2);
system("pause");
return 0;
}
![](https://img-blog.csdnimg.cn/20200514134340820.png)
3.5.2 读文件大小
思路:
1 文件指针移动到末尾,fseek(fp3, 0L, 2);
2 ftell(fp3) 告诉你字节数,这个就可以看做文件的大小
3 再读文件的内容,指针要先移动到文件开头,fseek(fp3, 0L, 0);
4 用 calloc 分配内存给字符串
5 fgets(str, length, fp); 读取文件,可以读取空格
6 puts(str)
calloc 与 malloc 的区别见:https://blog.csdn.net/qq_40893824/article/details/103454509 - 3.1 小节
出现的问题
一般的内容可以用 fgets() 读取,若是 fwrite() 写入的内容,fgets() 只能读取第 1 个信息 就结束读取了…
#include<bits/stdc++.h>
using namespace std;
typedef struct Student
{
char name[10];
char sex[3];
char age[3];
}Student;
int main()
{
// 读文件大小
FILE *fp3;
char path[] = "./text.txt" ;
fp3 = fopen(path, "r");
if(fp3 == NULL)
{
cout << "can not open the " << path + 2 << "!" << endl << endl;
exit(0);
}
else
{
fseek(fp3, 0L, 2); // fp3 移动到文件末尾了
int length = ftell(fp3);
cout << "length = " << length << endl;
fseek(fp3, 0L, 0); // fp3 移动到文件开头了
char *str = (char *) calloc(length + 1, sizeof(char)); // 注意和 malloc的不同!
fgets(str, length, fp3);
cout << endl << "str = " << endl;
puts(str);
cout << endl;
}
fclose(fp3);
system("pause");
return 0;
}
3.6 重定向
重定向:输入输出不在屏幕上进行
输入从文件读取,输出想文件写入
事先有个 in.txt,内容是“12 13”
#include<bits/stdc++.h>
using namespace std;
int sum(int a, int b)
{
return a + b;
}
int main()
{
char path1[] = "./in.txt" ;
char path2[] = "./out.txt";
freopen(path1, "r", stdin);
freopen(path2, "w", stdout);
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n",sum(a, b));
// system("pause");
return 0;
}
![](https://img-blog.csdnimg.cn/20200514150610438.png)
注意
system(“pause”);这个语句不要写进去
写的后果:
4 关闭
fclose(文件指针);