文件操作
文件分类
- 文本文件
文本文件是以ASCII码存储的
- 二进制文件
二进制文件是以二进制存储的
- 两者区别
注意点: 任何文本编辑器在打开文件的时候,都会以某种编码方式取解码存储的数据,而文本编辑器默认的解码方式就是ASCII解码
1.存储步骤不同:
文本文件在存储的时候,会先查询需要存储数据的ASCII码,然后再将对应的ASCII码转换为二进制,然后再存储
二进制文件在存储的时候,会直接转换为二进制存储
2.从存储步骤来看,文本文件需要先查找再存储,所以效率会低一些
3.从内存上来看,文本文件更占内存
文件的打开和关闭
重点: 文件打开模式重点记住w+和a+模式,而且在读写操作时候,文件打开的模式在windows平台要以wb+,rb+这种方式书写
#include <stdio.h>
int main()
{
/*
* 文件的打开和关闭
* 1. 文件打开函数 fopen(const char * filename,const char * mode)
* 第一个参数: 需要打开文件的名称
* 第二个参数: 打开文件的模式
* 返回值: 如果打开文件成功,会返回文件的句柄
*
*
* 2.文件关闭函数 用来关闭先前fopen()打开的文件
* fclose(想要关闭的文件);
* 返回值: 成功返回0, 失败返回EOF(-1)
*/
//文件打开函数模式
//1. r模式,读取文件
// 文件不存储会报错,文件存储就打开, 只能读不能写
FILE *fp = fopen("lnj.txt","r");
//2. w模式,写入文件
// 文件不存储会自动创建,会将存储的文件直接打开, 只能写不能读
//3. r+模式,读取和写入文件
// 文件不存储会报错,文件存在就打开, 既可以读也可以写
//4. w+模式,读取和写入文件(重点)
// 文件不存储会自动创建,文件存在就直接打开, 既可以读也可以写
//5. a模式,追加写入文件
// 文件不存储会自动创建,文件存在就直接打开, 只能写不能读
//6. a+模式,追加写入文件,并且可以读取(重点)
// 文件不存储会自动创建,文件存在就直接打开, 可以在原有的数据的基础上追加,并且还可以读取数据
//关闭文件
fclose(fp);
return 0;
}
FILE 结构体
fopen函数返回的是FILE *是一个指向结构体的指针
FILE * 并不是打开的那个文件,而是一个结构体,这个结构体中描述了被打开文件在缓冲区中的各种状态
FILE结构体
struct _iobuf {
char *_ptr; //文件输入的下一个位置
int _cnt; //当前缓冲区的相对位置
char *_base; //文件的起始位置)
int _flag; //文件标志
int _file; //文件的有效性验证
int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
int _bufsiz; // 缓冲区大小
char *_tmpfname; //临时文件名
};
typedef struct _iobuf FILE;
文本文件
一次写入和读取一个字符
- 写入
#include <stdio.h>
int main()
{
/*
* 一次写入一个字符
* fputc这个函数就是一次写入一个字符,fputc是以文本文件的形式保存数据
* 第一个参数: 需要写入到文件的字符
* 第二个参数: 已经打开的文件句柄
* 返回值: 写入成功,返回写入成功字符,如果失败,返回EOF(-1)本质就是-1
* 注意点: w/w+ 如果文件中有内容会覆盖之前的内容
*/
//1. 先打开一个文件
FILE *fp = fopen("it.txt", "w");
//2. 往文件中写入一个字符
int res = fputc('a', fp);
printf("%i\n", res);
//3. 关闭打开的文件
fclose(fp);
return 0;
}
- 读取
#include <stdio.h>
int main()
{
/*
* 一次读取一个字符
* fgetc()函数
* 第一个参数: 被读取的文件的句柄
* 返回值: 当前读取到的字符,如果失败就返回EOF
* 注意点:fputc函数每次写入一个字符,指针就会往后面移动一位
* fgetc函数每次读取后,指针就会往后移动一位
*/
FILE *fp = fopen("it.txt", "w+");
fputc('a',fp);
fputc('b',fp);
fputc('c',fp);
// 将头文件指针重新指向一个流的开头
rewind(fp);
// char ch = fgetc(fp);
// printf("ch = %c\n",ch);
char ch;
while((ch = fgetc(fp)) != EOF){
printf("ch = %c\n",ch);
}
return 0;
}
- 简单的加密解密练习
#include <stdio.h>
void encode(char *name, char *newName,int code);
int main()
{
/*
* 简单的文件加密和解密
* 思想: 利用异或一个数组两次会得到它本身
*/
encode("it.txt","itencode", 5);
encode("itencode","itdecode", 5);
return 0;
}
void encode(char *name, char *newName,int code){
//1. 打开一个需要加密的文件
FILE *fr = fopen(name,"r+");
//2. 打开一个需要写入的加密内容的文件
FILE *fw = fopen(newName,"w+");
//3. 不断的读不断的加密文件写入
char ch = EOF;
while ((ch = fgetc(fr)) != EOF) {
ch = ch ^ code;
fputc(ch, fw);
}
//4. 关闭已经打开的文件
fclose(fr);
fclose(fw);
}
一次性写入多个字符
- 写入
fputs函数是以文本文件形式存储,存储的是ASCII
如果利用fputs函数写入字符遇到\0会终止写入
#include <stdio.h>
int main()
{
/*
* 一次性写入一行数据
* fputs函数可以一次写入一堆字符
* 第一个参数: 需要写入的数据
* 第二个参数: 写入到的文件的句柄
* 注意点: 不会在写入字符的后面自动添加\n
写入的时候遇到\0会停止写入
*/
//1. 打开一个文件
FILE *fp = fopen("test.txt", "w+");
fputs("helloworld\n",fp);
fputs("helloworld\n",fp);
//关闭打开的文件
fclose(fp);
return 0;
}
- 读取
#include <stdio.h>
int main()
{
/*
* 一次性读取一行数据
* fgets()函数是一次读取一行数据
* 第一个参数: 读取出来的数据会放到这个参数的变量中
* 第二个参数: 需要读取多少个字符的数据
* 第三个参数: 被读取文件的句柄
*
* 注意点:
* 1.虽然告诉系统需要读取1024个字符,但是只要遇到\n,fgets()函数会自动终止读取
* 2. 每次只能读取n-1个字符,读取完成会在末尾自动添加\0
* 3. 遇到EOF也会自动停止读取
*/
//打开一个文件
FILE *fp = fopen("test.txt", "r+");
// 定义一个字符数组保存读取的内容
char buf[1024];
fgets(buf, 1024, fp);
return 0;
}
- 读取多行内容
#include <stdio.h>
int main()
{
/*
* 使用fgets一次读取多行内容
* 返回值: char * 正常返回字符指针,出错或遇到文件结尾返回空指针NULL
*/
FILE *fp = fopen("test.txt", "r+");
char buf[1024];
//一次读取多行内容
while ((fgets(buf, 1024, fp)) != NULL) {
printf("%s", buf);
}
return 0;
}
二进制文件读写操作
普通数据读写
- 写入
#include <stdio.h>
int main()
{
/*
* 用于操作二进制文本的读写函数
* fwrite / fread这两个函数就是用于操作二进制文本的
*
* fwrite函数 一次性写入一块数据
* 第一个参数: 需要写入文件的数据地址
* 第二个参数: 每次写入多少个字节
* 第三个参数: 需要写入多少次
* 第四个参数: 被写入文件的句柄
返回值: 写入多少次就返回多少,如果出错或者文件结束就返回0
*
* 注意点: 在二进制当中,是没有\0 \n这些转义字符的概念,写入的受到这些字符的影响
一般写入数据,每次写入多少个字节写大点,写入次数写少点
*/
FILE *fp = fopen("abc.txt", "w+");
int age[] = {1,3,5};
//将age中sizeof(age)个字节一次性写入到对应的fp文件当中
fwrite(&age, sizeof(age), 1, fp);
return 0;
}
- 读取
#include <stdio.h>
int main()
{
#include <stdio.h>
int main()
{
/*
* 一次性读取一块内容
* fread()函数
* 第一个参数: 读取的数据存储到哪
* 第二个参数: 一次读取多少个字节
* 第三个参数: 一共读取多少次
* 第四个参数: 被读取文件的句柄
* 返回值: 读取多少次就返回多少,如果出错就返回0
*
* 注意: 虽然告诉系统需要读取1024次,但是只要读取到EOF就不会再读取了
* 一般情况下读取一块数据,每次都按照读取数据类型来接收,一共读取多少次按照字节数来填写
*
*/
FILE *fp = fopen("def.txt", "r+");
char buf[1024] = {0};
fread(buf, 1, 1024, fp);
printf("buf = %s\n", buf);
return 0;
}
结构体读写操作
- 读写普通结构体
#include <stdio.h>
//定义一个结构体
typedef struct person
{
char name[8];
int age;
double height;
} Person;
int main()
{
/*
* 二进制读写对结构体操作
*/
//1. 往文件中写入一个结构体
Person p = {"wjh", 18, 1.7};
FILE *fp = fopen("per.txt", "wb+");
fwrite(&p, sizeof(Person), 1, fp);
fclose(fp);
//2. 读取一个文件中的结构体
Person p;
FILE *fp = fopen("per.txt", "rb+");
fread(&p, sizeof(p), 1, fp);
printf("name = %s\n", p.name);
printf("age = %i\n", p.age);
printf("height = %lf\n", p.height);
fclose(fp);
return 0;
}
- 读写结构体数组
#include <stdio.h>
//定义一个结构体
typedef struct person
{
char name[8];
int age;
double height;
} Person;
int main()
{
/*
* 读写结构体数组
*/
// Person ps[3] = {
// {"lnj", 20, 1.7},
// {"wjh", 18, 1.8},
// {"niuzi", 17, 1.6},
// };
//1.往文件中写入结构体数组
// FILE *fp = fopen("ps.txt", "wb+");
// fwrite(&ps, sizeof(ps), 1, fp);
// fclose(fp);
//2. 读取文件中结构体数组
FILE *fp = fopen("ps.txt", "rb+");
Person ps[3];
fread(&ps, sizeof(Person), 3, fp);
for(int i = 0; i < 3; i++){
Person p = ps[i];
printf("name = %s\n", p.name);
printf("age = %i\n", p.age);
printf("height = %lf\n", p.height);
}
return 0;
}
链表读写
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct person
{
char name[8];
int age;
double height;
struct person *next;
} Person;
Person* createEmpty();
void insertNode(Person *head, Person per);
void printList(Person *head);
void saveList(Person *head);
Person *loadList(char *name);
int main()
{
/*
* 二进制读写对链表操作
*/
//1. 创建一个空链表
Person *head = createEmpty();
//2. 往空链表中插入数据
insertNode(head, (Person){"lnj", 19, 1.8});
insertNode(head, (Person){"wjh", 13, 1.6});
insertNode(head, (Person){"niuzi", 20, 1.5});
//3. 增删改查操作
// printList(head);
//4.将链表保存到文件中
// saveList(head);
//5. 从文件中读取链表
loadList()
//6. 增删改查操作
return 0;
}
Person *loadList(char *name){
//1. 创建一个空链表
Person *head = createEmpty();
//2. 打开文件
FILE *fp = fopen(name, "rb+");
//3. 创建一个节点
Person p;
while (fread(&p, sizeof(Person), 1, fp) > 0) {
insertNode(head, p);
}
}
/**
* @brief printList 遍历链表
* @param head 链表的头指针
*/
void printList(Person *head){
// 头节点对我们来说没有用,头节点没有保存数据
//1. 取出头节点的下一个节点
Person *cur = head->next;
//2. 判断是否为NULL,如果不为NULL就开始遍历
while(cur != NULL){
//2.1 取出当前数据打印
printf("name = %s\n", cur->name);
printf("age = %i\n", cur->age);
printf("height = %lf\n", cur->height);
//2.2 让当前节点往后移动
cur = cur->next;
}
}
/**
* @brief saveList 将有效节点写入到文件
* @param head 链表的头指针
*/
void saveList(Person *head, char *name){
//1. 打开文件
FILE *fp = fopen(name, "wb+");
//2. 定义指针变量保存头节点的下一个节点
Person *cur = head->next;
while (cur != NULL){
//写入一个有效的节点
fwrite(cur, sizeof(Person), 1, fp);
// 让指针往后移动
cur = cur->next;
}
//3. 关闭文件
fclose(fp);
}
/**
* @brief insertNode 封装一个插入新节点的函数
* @param head 头节点
* @param num 插入新节点的数据
*/
void insertNode(Person *head, Person data){
//1. 创建一个新节点
Person *node = (Person *)malloc(sizeof(Person));
//2. 将数据保存到新节点中
for(int i = 0; i < strlen(data.name); i++){
node->name[i] = data.name[i];
}
node->age = data.age;
node->height = data.height;
//3. 插入节点
//3.1 将新节点的下一个节点 等于 头节点的下一个节点
node->next = head->next;
//3.2 让头节点的下一个节点 等于 新节点
head->next = node;
}
/**创建空链表的函数
* @brief createList
* @return
*/
Person* createEmpty(){
//1. 定义头指针
Person *head = NULL;
// 空链表只有头指针和一个节点,并且节点没有数据,也有没有下一个节点
//2. 创建一个空节点,并赋值给头指针
head = (Person *)malloc(sizeof(Person));
head->next = NULL;
//3.返回头指针
return head;
}
网友评论