数据结构9.1 - 文件基础(C)

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 知识点

  1. 文件指针会自动移动
  2. 文件末尾是EOF
  3. 下面的对象都是文件
    写文件是 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);
  4. fputc(字符ch, 文件指针); // 写:字符ch写进文件
  5. 字符ch = fgetc(文件指针); // 读:读文件内容,赋给字符ch
  6. fputs(str, 文件指针); // 写:字符串str写进文件
  7. fgets(buff, 长度, 文件指针); // 读:读文件内容,赋给buff,长度是buff的容量
  8. fputs() 与 fgets() 是把真正的字符串写进文件,不包含最后的’\0’,若要在文件中换行,fputs("\n", fp); 即可
  9. 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() 不能读空格!
  10. fwrite(地址, 个数, 字节, fp);
    fread(地址, 个数, 字节, fp);
    字节 和 个数 顺序可以反过来
  11. freopen(path, “r”, stdin); 输入信息从 path 路径的文件中获取,而不是键盘
    freopen(path, “w”, stdout); 输出 向 path 路径的文件中写入,而不是屏幕

3.1 字符 读写文件

  1. fputc(字符ch, 文件指针); // 写:字符ch写进文件
  2. 字符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;
}

3.2 字符串 读写文件

  1. fputs(str, 文件指针); // 写:字符串str写进文件
  2. 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;
}

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;
}

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;
}

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;
}

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;
}

注意
system(“pause”);这个语句不要写进去
写的后果:

4 关闭

	fclose(文件指针);
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_1403034144

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值