文件操作
1.1 概念:
①文件:一切存储在存储介质上的数据集合。
②狭义文件:具有指定的路径,有名称,有数据存储格式
1.2 学习目的
使用C语言库函数来对于指定的文件进行操作,实现文件中数据的自动读取和写入等功能。
原则:操作系统会将一切能够识别的存储数据集合当做文件(文本,源代码,目录,硬件设备等)
1.3流
在进行数据的输入输出处理时,硬件设备的多样性和复杂性,会导致使用程序访问这些硬件设备时难度过大及不方便。因此,为了方便程序和硬件交互,标准C将硬件设备抽象成“标准输入输出设备”这样一个抽象的概念,即忽略了具体的硬件设备,仅仅抽象出交互的接口,此时计算机就可以不再考虑(依赖)具体的硬件设备而只是关心抽象的输入输出设备统一接口。就会使得程序设计者只用关系具体任务实现,而不用再关系具体的硬件设备。
这种不依赖于任何具体的IO(input,output)设备的一个统一的访问文件的接口,被称为流。而经过抽象出的那种“标准输入输出设备”也称为“标准文件”,用于访问此类设备的流称为“标准输入输出流”
1.4 标准输入输出设备
凡是符合ANSIC的设定标准,能够实现内容的读取和写入的设备,都被称为标准输入输出设备
屏幕 键盘 打印机 扫描仪……
1.5 流的分类
数据流动方向:输入流和输出流
数据形式:文本流和二进制流
文本流数据格式是ASCII码,二进制流数据格式是二进制值
1.6 标准输入输出流
C语言文件管理系统将标准输入和标准输出设备抽象成了标准输入输出流。在标准C中,这种流有三种形式:
stdin 标准输入 (键盘等输入设备)
stdout 标准输出 (屏幕,打印机)
stderr 标准错误输出
综上所述,软件开发者需要使用流这种数据结构来操作文件。
1.7 标准IO的函数
通过C语言库函数进行的IO操作被称为标准IO。
操作文件的步骤:1 打开文件 2 读写文件 3 关闭文件
① 打开文件
FILE *fopen(const char *path, const char *mode);
参数 path 文件路径
mode 文件操作权限
r 只读 rb 只读二进制文件
w 只写,清空文件内容并写入 wb 只写二进制文件
r+ 读写打开,文件必须存在 rb+ 对二进制文件读写
w+ 读写打开,文件不存在则创建,存在则清空 wb+ 对二进制文件读写
a 追加写方式打开,文件不存在则创建 ab 对二进制文件只写追加
a+ 可读并可追加写方式打开 ab+对二进制文件读写追加
返回值:流指针,利用该指针变量来通过流访问到文件
该结构的具体内容请参考stdio.h原码或课本P71
②关闭文件
int fclose(FILE *fp);
参数: fp 文件流指针
返回值:0代表函数执行成功
③ 文件内容操作函数(读 写 其他)
字符串操作函数
将format中的内容赋值进str中
int sprintf(char *str, const char *format, ...);
参数: str 目标字符串缓冲区首地址
format 格式化说明符字符串
… 参数
char str[100] = "";
int a = 100;
double b = 99.99;
char c = 'K';
sprintf(str, "###%d$$$%lf***%c\n", a, b, c);
printf("%s\n", str);
int fprintf(FILE *stream, const char *format, ...);
参数: stream 文件流指针
format 格式化字符串
… 附加参数
返回值:成功输出到指定文件的字节数
int a = 100;
double b = 99.99;
char c = 'K';
fprintf(stdout, "###%d$$$%lf***%c\n", a, b, c);
读取格式化数据函数
int fscanf(FILE *stream, const char *format, ...);
参数:stream 流指针
format 格式化字符串
。。。 参数
返回值:实际读取的字节数
向文件中写入数据
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
参数: ptr 要写入内容的缓冲区
size 写入的一个单元的大小
nmemb 写入的单元个数
stream 文件流指针
返回值:实际写入的单元的个数
从指定文件中读取数据
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数 ptr 指向需要保存数据的缓冲区
size 一个单元的大小
nmemb 需要读取的单元数
stream 文件流指针
测试是否到达文件末尾
int feof(FILE *stream);
参数 stream流指针
返回值 非零值代表到达文件末尾
从文件中读取一个字节
int fgetc(FILE *stream);
参数:文件流指针
返回值:实际读取的字节数 ,EOF文件末尾
#include <stdio.h>
#include <stdlib.h>
#if 1
int main()
{
FILE* fp = NULL;
char str[5] = "";
char c;
//fscanf(stdin, "%s", str);
//scanf("%s", str);
int res;
fp = fopen("E:/2407/Day19/LoveStory.txt", "r+");
if (fp != NULL)
{
printf("文件打开成功\n");
}
else
{
printf("文件打开失败\n");
exit(-1);
}
//res = fwrite("abc", 3, 1, fp);
//printf("res= %d\n", res);
/*
一次读取 保证缓冲区大小大于等于文件内容
res = fread(str, sizeof(str)-1, 1, fp);
printf("res = %d\n", res);
puts(str);
*/
/*
while (feof(fp)==0)
{
res = fread(str, sizeof(str)-1, 1, fp);
printf("%s", str);
memset(str, 0, sizeof(str));
}
*/
while ((c = fgetc(fp)) != EOF)
{
putchar(c);
}
return 0;
}
#endif
写入单个字符到文件中
int fputc(int c, FILE *stream);
参数: c 要被写入的数据
stream 文件流指针
返回值: 实际写入字符内容
1.8文件缓冲区
1.8.1 概念:
在使用标准IO时,无论是是输入还是输出,系统会自动的为当前文件在内存中开辟一个缓冲区,从内存向文件中保存数据或从文件向内存中写入数据时,都会先将数据送入进缓冲区,然后再从缓冲区中将数据移动到对应的地方。
1.8.2 缓冲区存在的原因
需要提高IO效率,减少频繁的IO操作。
如果没有缓冲区意味着每次写入或者读取一个字节,都会调用底层的IO操作,当大量读写时,底层的IO操作会被频繁调用。因此,C语言提供了在IO时所用到的缓冲区,现将数据保存到缓冲区中,然后当缓冲区满或者某一条件满足时,然后再一起将数据进行读出或写入进文件。
当缓冲区满(== 4096字节)就会将缓冲区的内容取出,清空缓冲区
1.8.3 强制刷新缓冲区
立刻将缓冲区中的内容取出,并清空缓冲区
int fflush(FILE *stream);
参数: stream 文件流指针
返回值: 0 代表成功
对于stdout来说,该函数会强制的将输出缓冲区的内容取出执行输出操作,并清空缓冲区
对于stdin来说,会强制的丢弃缓冲区的内容
1.8.4 设置缓冲区
void setbuf(FILE *stream, char *buf);
参数:stream 流指针
buf 指向文件流所使用的缓冲区指针
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
参数:stream 流指针
buf 指向文件流所使用的缓冲区指针
mode _IOFBF 全缓冲
_IOLBF 行缓冲
_IONBF 不缓冲
size 缓冲区大小
1.8.5 设置文件读写偏移量(文件位置指示器)
概念:在使用标准IO对文件进行读写操作时,C语言底层会为当前的读写文件操作提供一个类似于游标这么一个结构,目的是记录程序在运行时,读取或写入的过程中,识别到文件的第几个字节了。
API
long ftell(FILE* stream);
参数: 流指针
返回值:文件位置指示器
重置文件读写偏移量
int fseek(FILE *stream, long offset, int whence);
参数: stream 文件流指针
offset 偏移量 >0 向文件末尾偏移
<0 向文件开头处偏移
==0 不偏移
whence 开始偏移的位置
SEEK_SET 从文件开头处
SEEK_CUR 从当前文件位置指示器处
SEEK_END 从文件末尾处
fseek(fp, 0, SEEK_SET);//将文件读写偏移量重置到文件开头
fseek(fp, 20, SEEK_END);
//将文件读写偏移量重置到文件末尾再向后移动20字节,这样产生的文件叫做空洞文件