C语言——文件操纵
目次媒介
一什么是文件
1步伐文件
2数据文件
3文件名
二文件的打开与关闭
1文件指针
2fopen
3fclose
三文件的读与写
1文件的次序读写
1.1fputc fgetc
1.2fputs fgets
1.3fprintf fscanf
1.4fwrite fread
1.5文本文件和二进制文件
2文件的恣意读写
1fseek
2ftell
3rewind
四文件读取竣事
五文件缓冲区
六改写通讯录
媒介
在前面写通讯录时,我们发现:每次步伐竣事后通讯录内的消息就不见了,缘故因由是:信息都是在内存中储存;如果我们要想把信息存储起来,就必要和文件打交道,那么C语言中又是怎么对文件举行操纵呢?看完这篇文章大概你就懂了!
一什么是文件
磁盘上生存的数据都叫做文件;
在步伐计划中,我们一样寻常谈的文件有两种:步伐文件、数据文件;
1步伐文件
包罗源步伐文件(后缀为.c),目的文件(后缀为.obj),可实行步伐(后缀为.exe)
2数据文件
文件的内容不肯定是步伐,而是步伐运行时读写的数据; 比如步伐运行必要从中读取数据的文件,大概输出内容的文件
3文件名
用来标识文件唯一性;文件名通常是一个完备的文件路径(c:\code\test.txt)
二文件的打开与关闭
1文件指针
缓冲文件体系中,有个概念叫“文件范例指针”,简称“文件指针”; 打开一个文件时都会在内存中开辟一个结构体,用来储存文件的干系信息(文件名,巨细...),C语言把这种结构体界说为FILE:
struct _iobuf {
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
};
typedef struct _iobuf FILE; 而FILE通常是以指针的情势: 一是步伐员方便利用;
二是在内存中大概有许多FILE结构体,要想更好地管理它们:用类似链表的方式实现
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvYWUzOWZkZDJlMjc5NDg5OWFhMDZjY2IzMDc2MWMzNmMucG5n
2fopen
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvOGE0M2E1MTJlYmU1NDBhNWE2ZGEzZDI4MDk2ZmY2M2UucG5n
第一个参数是文件名(可以是相对路径(.\ ..\举行表现)大概绝对路径(完备路径));
第二个参数是你要用什么方式打开文件?
文件利用方式寄义如果指定文件不存在“r”(只读)为了输入数据,打开一个已经存在的文本文件堕落“w”(只写)为了输出数据,打开一个文本文件创建一个新的文件“a”(追加)向文本文件尾添加数据创建一个新的文件“rb”(只读)为了输入数据,打开一个二进制文件堕落“wb”(只写)为了输出数据,打开一个二进制文件创建一个新的文件“ab”(追加)向一个二进制文件尾添加数据堕落“r+”(读写)为了读和写,打开一个文本文件堕落“w+”(读写)为了读和写,发起一个新的文件创建一个新的文件“a+”(读写)打开一个文件,在文件尾举行读写创建一个新的文件“rb+”(读写)为了读和写打开一个二进制文件堕落“wb+”(读写)为了读和写,新建一个新的二进制文件创建一个新的文件“ab+”(读写)打开一个二进制文件,在文件尾举行读和写创建一个新的文件 3fclose
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvYzhhZDVlYTYzNTMyNDJmZjk1NDhjYzg1YTY3YzZkNDIucG5n
与申请内存类似:文件操纵完后也要对文件举行关闭
利用:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("fopen error");
return 1;
}
//使用...
fclose(pf);
pf = NULL;
return 0;
} 留意:对于文件后缀名:发起把它举行开启,否则可以会出现以下错误:
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvNmE1N2JmYWQ4NGI5NDJmOWI4NDAwNTA5YjNlNGRkZGIucG5n
明显在当前路径下有该文件,就是找不到!原来该文件的后缀名是被潜伏掉了,真正的文件名是:
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMWU0ODg4MWI2NGNiNGQ5YWFlYmFmODEwM2VlNTQ2ZDEucG5n
三文件的读与写
1文件的次序读写
功能函数名实用于字符输入函数fgetc全部输入流字符输出函数fputc全部输出流文本行输入函数fgets全部输入流文本行输出函数fputs全部输出流格式化输入函数fscanf全部输入流格式化输出函数fprintf全部输出流二进制输入fread文件二进制输出fwrite文件 这里所说的:输入输出流是怎么一回事?
流是一种抽象的概念,我们可将其当作‘水流’(数据流):当我们要接水时:我们可以用盆接,用碗接,用手接...同样,我们要吸收数据(输入流)也有多种方式,fgetc,fgets...用水的环境也有多种,我们利用数据(输出流)处置惩罚详细环境的方式也有所差别,决定了步伐员在差别环境下要把握差别函数调用来利用数据的本领;常见的输入输出流的例子: 我们寻常说:用scanf吸收数据,把数据打印到屏幕上:本质上就是输入输出流;那么你大概会说:我在利用scanf大概printf时是不消传入FILE*的指针变量就能完成,这怎么大概是输入输出流?
着实:在步伐运行时,步伐默以为我们打开了三个流:stdout(标准输出流),stdin(标准输入流),stderror(标准错误流),这三个变量都是FILE*范例!
利用scanf大概printf时底层默认利用指定的标准流来处置惩罚数据的~
1.1fputc fgetc
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMmRjOTk0NjU4MGY4NGY2Y2IwYTUxOWUyZGEyYTk1NmQucG5n
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvZGY3MmE5OTliMDY1NDU4NmJiZDExMGQ1Y2E5M2UyMmUucG5n
向指定流内里写入/读取一个字符
利用:data.txt文件写入26个字母
int main()
{
FILE* pf1 = fopen("data.txt", "w");
if (pf1 == NULL)
{
perror("fopen error");
return 1;
}
char ch = 0;
for (ch = 'a'; ch <= 'z'; ch++)
{
fputc(ch, pf1);
}
fclose(pf);
pf = NULL;
return 0;
} https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMWEwMjQ5ZDY4YWE0NDI4ZmJjMTUyZDI3NjYzNmNlNzcucG5n
data.txt文件读取26个字母
int main()
{
FILE* pf = fopen("data.txt", "r");//注意这个文件操作的方式是"r"
if (pf == NULL)
{
perror("fopen error");
return 1;
}
char ch = 0;
//fgetc读到文件结尾后返回EOF(-1)
while (1)
{
ch = fgetc(pf);
if (ch == EOF) break;
printf("%c ", ch);
}
fclose(pf);
pf = NULL;
return 0;
} https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvYzY5M2VmMGViMmYzNDY4NWIxYzEwNGNmNmMxZjYyN2QucG5n
利用stdout则将数据输出到屏幕上,到达与printf一样的效果
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvZGEzNzFkMTUwODg3NDE4Njg4MzVmNmY5MGIxYmUyM2YucG5n
1.2fputs fgets
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvZjcxN2FjZmNjMWQxNDRjNjk2MGJlYWYyYWU4Mjg3ZmYucG5n
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMDhkMTM5NDEwMTcyNGU4YjlmMzcwNjJmMjRhOWRkOWUucG5n
向指定流里写入/读取一行字符串
向data.txt中写入两行字符串
int main()
{
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("fopen error");
return 1;
}
fputs("hello\n", pf);
fputs("world\n", pf);
fclose(pf);
pf = NULL;
return 0;
} https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvY2NhYjEyOGFhZjc5NGY0ZWJhYTdlM2E4ZDJjOTJhY2UucG5n
从data.txt读取第一行的三个字符
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen error");
return 1;
}
char arr = { 0 };
fgets(arr, 3+1, pf);//读取的字符总数包含了\n,所以决定读n个字符参数传n+1
printf("%s", arr);
fclose(pf);
pf = NULL;
return 0;
} https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMTA4ZGVjZWJjNjVlNGU2M2E0Y2Q5YjlhNDg2ODg3NzgucG5n
1.3fprintf fscanf
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvM2ZiMmZiMTkxNGVlNGQ1YzljNmQ3YmFkMzZjMTI1ZGYucG5n
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvYWFjZTU5ZTc5MGUwNDU4MWJkY2UzMDQ2OTM4ZjliZWUucG5n
格式化数据(如结构体)写入/读取数据
将格式化数据写入data.txt
#include<stdio.h>
struct S
{
float f;
char c;
int i;
};
int main()
{
FILE* pf = fopen("data.txt", "w");
if (pf == NULL)
{
perror("fopen error");
return 1;
}
struct S s = { 3.14,'w',100 };
fprintf(pf, "%f-%c-%d", s.f, s.c, s.i);
fclose(pf);
pf = NULL;
return 0;
} https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvY2IwNDc2N2JhY2M3NGE4NWI0NmY2ZjdhYjZkOTM4NjIucG5n
把data.txt里的格式化数据读出来(读出的格式必要与写入的格式保持同等)
#include<stdio.h>
struct S
{
float f;
char c;
int i;
};
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen error");
return 1;
}
struct S s = {0};
//fprintf(pf, "%f-%c-%d", s.f, s.c, s.i);
fscanf(pf, "%f-%c-%d", &(s.f), &(s.c), &(s.i));//格式保存一致
printf("%f %c %d", s.f, s.c, s.i);
fclose(pf);
pf = NULL;
return 0;
} https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvY2M3MzQyMTY5ZTMxNGY0ZTlhZTExZjQyOWIxMjI5MTYucG5n
1.4fwrite fread
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMzMyNzBhZjY0M2JlNDJkM2FhMGI3ZTU0MDE3YzlhZTAucG5n
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvZDU0NjE0MjM4ZDg0NGZkZWIyZmRjMTFkNGE2M2Y4MjEucG5n
以二进制格式写入/读取
利用fwrite向文件写进5个数:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "wb");
if (pf == NULL)
{
perror("fopen error");
return 1;
}
int arr[] = { 1,2,3,4,5 };
fwrite(arr, sizeof(int), 5, pf);
fclose(pf);
pf = NULL;
return 0;
}
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvYTlkOGZjYTBkM2ZjNDE4NWIwM2IwNWUyMDFlYjdjM2IucG5n
发现都是乱码(二进制),那这些到底是不是我们写进去的数据呢?
用fread读取后打印出来看看:
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "rb");
if (pf == NULL)
{
perror("fopen error");
return 1;
}
int arr = {0};
fread(arr, sizeof(int), 5, pf);
for (int i = 0; i < 5; i++)
{
printf("%d ", arr);
}
fclose(pf);
pf = NULL;
return 0;
} https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvYmEwMmJlMTZlZTZjNGUwN2JlMDk4YjYzNjIwNmNhMjQucG5n
前面的三组函数都是以文本文件举行写入/读取,而fwrite/fread以二进制文件举行写入/读取,这两者要怎么明白?
1.5文本文件和二进制文件
根据数据的构造情势,数据文件被称为文本文件大概二进制文件。
数据在内存中以二进制的情势存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII码的情势存储,则必要在存储前转换。以ASCII字符的情势存储的文件就是文本文件。
如果用一个整数以二进制格式写到文件中,要想看到对内容举行‘翻译’:可以将二进制文件拖到VS中以二进制编译器的情势打开:内容是整数在内存中的储存情势(vs:小端储存的十六进制)
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMDUzN2NlNGViNzk0NGQxNzg3OTJkZjljNzUwMjFkMzQucG5n
#include <stdio.h>
int main()
{
int a = 10000;
FILE* pf = fopen("test.txt", "wb");
fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
fclose(pf);
pf = NULL;
return 0;
} https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMmUzZGY3ZjdiNmNkNGZhOGIzYTFhOGMxYzQ5NTQzMDcucG5n
2文件的恣意读写
1fseek
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvZmJmZjZhOTIzOTlmNDk4ZjhkMmQ5Yjk5ZmY5ZjEzNjcucG5n
根据文件指针的位置和偏移量来定位文件指针(想读哪个文件指针就定位到那里)
关于origin的参数:
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMGVjOWY2ZmE0NWVkNDhhOGIwYjk5Y2U5OWMzZmE0Y2QucG5n
利用:通过三个参数找到文件的末了一个字符
#include<stdio.h>
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen error");
return 1;
}
//data.txt的数据:abcde
char ch=fgetc(pf);
printf("%c\n", ch);
//读出最后一个字符e
//fseek(pf, -1, SEEK_END);//SEEK_END定位到的是\n,往前偏移一位才能找到e
//fseek(pf, 4, SEEK_SET);//开始位置完后偏移4找到e(要计算)
fseek(pf, 3, SEEK_CUR);//当前位置往后偏移3找到e(要计算)
ch = fgetc(pf);
printf("%c", ch);
fclose(pf);
pf = NULL;
return 0;
} 2ftell
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvN2RhNThiYTFiMDA5NDg2N2EwMGVlMzc3NjRiMmFlZDIucG5n
返回文件指针相对于起始位置的偏移量
3rewind
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMWM4ZTJjOTAxYzM5NDhjM2I1Y2JkZWE0NjEyNjUyNDcucG5n
让文件指针从起始位置开始
四文件读取竣事
网上大概课本上许多说:用 feof 函数举行判定文件是否竣事,着实是不严谨的; feof作用:当文件读取竣事后,判定是否遇到文件竣事标志而竣事 与ferrof作用对比: 当文件读取竣事后,判定是否遇到错误而竣事
1. 文本文件读取是否竣事,判定返回值是否为 EOF ( fgetc ),大概 NULL ( fgets );
2. 二进制文件的读取竣事判定,判定返回值是否小于实际要读的个数 (如果正常利用write/read 写入/读取多少个,正常来说是会返回写入/读到的个数)
利用:将test1.txt的内容拷贝到test2.txt中
#include <stdio.h>
int main(void)
{
int c; // 注意:int,非char
FILE* fp1 = fopen("test1.txt", "r");
if (!fp1)
{
perror("File opening failed");
return 1;
}
FILE* fp2 = fopen("test2.txt", "w");
if (!fp2)
{
perror("File opening failed");
fclose(fp1);
fp1 = NULL;
return 2;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp1)) != EOF) // 标准C I/O读取文件循环
{
fputc(c, fp2);
}
fclose(fp1);
fp1 = NULL;
fclose(fp2);
fp2 = NULL;
return 0;
} https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvNzY4ZWJmNTYxNGUwNGVmZDk4MTlhZGZiOTUxMWQ5NTkucG5n
五文件缓冲区
ANSIC 标准采取“缓冲文件体系”处置惩罚的数据文件的,所谓缓冲文件体系是指体系主动地在内存中为步伐中每一个正在利用的文件开辟一块“文件缓冲区”;
写入文件的数据先生存在“文件缓冲区”中,比及调用干系函数把文件缓冲区的数据革新到文件中(close()),才是真正地把数据生存在文件中
而scanf()读键盘数据也是同理:以是我们在用scanf读入字符串时要留意缓冲区的'\n'标题
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvNzYwOTdkNmU3OGQ3NDM2OGI5YTRhODhlN2M1Mjg1MjQucG5n
//测试文件缓冲区存在的代码
#include <stdio.h>
#include <windows.h>
//VS2013 WIN10环境测试
int main()
{
FILE* pf = fopen("test.txt", "w");
fputs("abcdef", pf);//先将代码放在输出缓冲区
printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
Sleep(10000);
printf("刷新缓冲区\n");
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
Sleep(10000);
fclose(pf);//fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0;
} 六改写通讯录
学习完文件操纵后,是时间办理媒介提出的标题:把通讯录改造成不受步伐关闭而通讯录信息丢失
1.在步伐退出时,把当前通讯录的信息生存在Contact.txt文件中(利用fwrite) 2.在步伐刚开始初始化通讯录时,把Contact.txt文件的数据加载到内存中(利用fread)
void CheckCapacity(Contact* con)
{
if (con->sz == con->capacity)
{
//扩容
Mess* ptr = realloc(con->message, (con->capacity + Default_Size) * sizeof(Mess));
if (ptr != NULL)
{
con->message = ptr;
con->capacity += Default_Size;
printf("扩容成功\n");
}
else
{
perror("realloc error");
return;
}
}
}
void InitContact(Contact* con)
{
//文件版本:对上一次的通讯录数据进行拷贝
con->message = (Mess*)malloc(Default_Size * sizeof(Mess));
if (con->message == NULL)
{
perror("malloc error");
return 1;
}
con->sz = 0;
con->capacity = Default_Capacity;
FILE* pf = fopen("Contact.txt", "rb");
if (pf == NULL)
{
perror("fopen fail");
return 1;
}
Mess tmp = { 0 };
while (fread(&tmp, sizeof(Mess), 1, pf))
{
CheckCapacity(con);
con->message = tmp;
con->sz++;
}
}
void SaveContact(Contact* con)
{
FILE* pf = fopen("Contact.txt", "wb");
for (int i = 0; i < con->sz; i++)
{
fwrite(con->message + i, sizeof(Mess), 1, pf);
}
} 以上便是全部内容,有标题欢迎在批评区指正,感谢观看!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]