C语言(五-文件)
标准IO库函数对磁盘文件得读取特点
1.jpg文件缓冲区是库函数申请得一段内存,由库函数对其进行操作,程序员没必要知道存放在哪里
缓冲区分类
- 行缓冲
标准io库函数,往标准输出(屏幕)输出东西得时候是行缓冲,所谓行缓冲就是缓冲区碰到换行符得时候才刷新缓冲区;默认行缓冲得大小为1024字节,如果满了即使不换行也会输出
- 全缓冲
标准io库函数,往普通文件读写数据得,是全缓冲,碰到换行符也不刷新缓冲区,即缓冲区满了,才刷新缓冲区
刷新缓冲区得情况
- 缓冲区满了,刷新缓冲区
- 人为刷新缓冲区fflush(文件指针)
- 程序正常结束 会刷新缓冲区
- 无缓冲
在读写文件得时候通过系统调用io(read write),对文件进行读写数据,这个时候是无缓冲得,即写数据会立马进入文件,读数据会立马进入内存
写文件流程
应用程序空间->内核空间->驱动程序->硬盘上
应用程序和内核程序运行在不同得空间里,目的是为了保护内核
设置缓冲区目的
通过缓冲可以减少进出内核得次数,以提高效率
磁盘文件得分类
一个文件通常是磁盘上一段名命得存储区,计算机得存储在物理上是二进制得,所以物理上所有磁盘文件本质上都是一样的:以字节为单位进行顺序存储,从用户或者操作系统使用得角度(逻辑上)把文件分为:
- 文本文件:基于字符编码得文件
- 二进制文件:基于值编码得文件
文本文件
基于字符编码,常见编码有ASCII,UNICODE等,一般可以使用文本编辑器直接打开
例如:数字5678得以ASCII存储形式为:
ASCII码:00110101 00110110 00110111 00111000
还有歌词文件(lrc):文本文件
二进制文件
基于值编码,自己根据具体应用,指定某个值得意思,把内存中得数据按其在内存中得存储形式原样输出到磁盘上,一般需要自己判断或使用特定软件分析数据格式
例如:数5678得存储形式为:
二进制码:00010110 00101110
音频文件(mp3):二进制文件
图片文件(bmp)文件,一个像素点由两个字节来描述*****######&&&&&
* 代表红色得值
# 代表绿色得值
& 代表蓝色得值
二进制文件以位来表示一个意思
对比
译码:
文本文件编码基于字符定长,译码容易;
二进制文件编码是变长得,译码难一些(不同得二进制文件格式,有不同得译码方式)
空间利用率:
二进制文件用一个比特来代表一个意思(位操作);
而文本文件任何一个意思至少是一个字符
可读性:
文本文件用通用得记事本工具就几乎可以浏览所有文本文件
二进制文件需要一个具体得文件解码器,比如BMP文件,必须用读图软件
文件指针
文件指针在程序中用来标识一个文件得,在打开文件得时候得到文件指针,文件指针就用来代表咱们打开得文件。
咱们对文件进行读写关闭等操作的时候,对文件指针进行操作即可,即咱们将文件指针,传给读写关闭等函数。
定义文件指针得一般形式:
FILE * 指针变量标识符;
FILE 为大写,需要包含<stdio.h>
FILE是系统使用typedef定义出来得有关文件信息得一种`结构体类型`,结构体中有文件名、文件状态和文件当前位置等信息
FILE在stdio.h文件中得文件类型声明:
typedef struct{
short level;//缓冲区满或空得程度
unsigned flags;//文件状态标志
char fd;//文件描述符
unsigned charhold;//如无缓冲区不读取字符
short bsize;//缓冲区大小
unsigned char *buffer;//数据缓冲区得位置
unsigned ar *curp;//指针,当前得指向
unsigned istemp;//临时文件,指示器
shorttoken;//用于有效性检查
}FILE;
在缓冲文件系统中每个被使用得文件都要在内存中开辟一块FILE
类型得区域,存放与操作文件相关得信息
对文件操作得步骤:
- 对文件进行读写等操作之前要打开文件得到文件指针
- 可以通过文件指针对文件进行读写等操作
- 读写等操作完毕之后,要关闭文件,关闭文件之后,就不能再通过此文件指针操作文件了
c语言中有三个特殊得文件指针无需定义,再程序中可以直接使用
- stdin:标准输入 默认为当前终端(键盘)
使用scanf、getchar函数默认从此终端获取数据
- stdout:标准输出 默认为当前终端(屏幕)
printf、puts函数默认输出信息到此终端
- stderr:标准错误输出设备文件 默认为当前终端(屏幕)
程序出错使用:perror函数是信息打印再此终端
打开文件
FILE *fopen(const char *path,const char *mode);
函数说明:fopen函数的功能是打开一个已经存在的文件,并返回这个文件的文件指针(文件的标志),或者创建一个文件,并打开此文件,然后返回文件的标示
参数1:打开的文件得路径
1. 绝对路径
2. 相对路径
参数2:文件打开的方式,(只读、只写、读写等等)
r w a +
注意:反是加b的都是操作二进制文件,但是一般都是文本文件读写,所以不常用
返回值:
成功,打开的文件对应的文件指针
失败:返回NULL
模式 | 功能 |
---|---|
r或rb | 以只读方式打开一个文本文件(不创建文件) |
w或wb | 以写方式打开文件(如果文件存在则使文件长度截断为0字节-清空,如果文件不存在-创建一个文件) |
a或ab | 以添加方式打开文件,即在末尾添加内容,当文件不存在时,创建文件用于写 |
r+或rb+ | 以可读、可写的方式打开文件(不创建新文件) |
w+或wb+ | 以可读、可写的方式打开文件(如果文件存在则使文件长度为0-清空,存在则创建一个文件) |
a+或ab+ | 以添加方式打开文件,打开文件并在末尾更改文件(如果文件不存在则创建文件) |
关闭文件
函数的定义:
int fclose(FILE *fp);
关闭fp所代表的文件
注意:一个文件只能关闭一次,不能多次关闭。
关闭文件之后就不能再文件指针对文件进行读写等操作了
返回值:
成功返回0
失败返回非0
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
fp=fopen("C:/User/fil.txt","r");
if(fp==NULL){
//直接错误退出
return -1;
}
fclose(fp);
return 0;
}
读写一个字符
函数定义:int fgetc(FILE *stream)
fgetc从stream所标志的文件中读取一个字符,将字节返回
返回值:
以t的方式:读到文件结尾返回EOF
以b的方式:督=读到文件结尾,使用feof判断结尾
函数的定义:int fputc(int c,FILE *stream)
fputc将c的值写道stream所代表的文件中
返回值:
如果输出成功,则返回输出的字节值
如果输出失败,则返回一个EOF
EOF是再stdio.h文件中定义的符号常量,值为-1
注意: 打开文件的时候,默认读写文件的位置在文件的开始,
如果以a的方式打开读写位置再文件的末尾;向文件在读取字节或者写入字节的时候,读写
位置会往末尾方向偏移,读写多少个字节,读写位置就往文件的末尾方向偏移多少个字节
读文件判断是否结束:==EOF则结束
读写一个字符串
函数定义:
char *fgets(char *s,int siez,FILE *stream);
函数说明:
从stream所指得文件中读取字符,在读取得时候碰到换行符或者是
碰到文件得末尾停止读取,或者是读取到size-1个字节停止读取,
在读取得内容后面会加一个\0,作为字符串得结尾
返回值:
成功返回目的数组得首地址,即S
失败返回NULL
函数定义:
int fputs(consy char *s,FILE *stream);
函数说明:
将s指向得字符串,写到stream所代表得文件中
返回值:
成功返回写入得字节数
失败返回-1
int main(int argc, char *argv[])
{
FILE *fp;
fp=fopen("C:/User/fil.txt","r");
if(fp==NULL){
//直接错误退出
return -1;
}
//读文件
char buf[32]="";
//fgets每次读取时最多读取文件一行内容,遇到换行符立刻返回
//如果想要读取的字节数小于一行内容,则只会读取第二个参数-1个字节,最后补\0
fgets(buf,8,fp);
printf("buf=%s\n",buf);
//写文件
fputs("66666666\n",fp);
fputs("nihao",fp);
fclose(fp);
return 0;
}
读文件
函数的定义:
size_t_fread(void *ptr,size_t_size,size_t_nmemb,FILE *stream);
函数说明:
fread函数从stream所标识的文件中读取数据,一块是size字节,
龚nmemb块,存放到ptr指向的内存里
返回值:
实际读到的块数
例子:
int num;
num=fread(str,100,3,fp);
从fp所代表的文件中读取内容存放到str指向的内存中,读取的字节数为,每块100字节,3块。
返回值num,如果读到300个字节返回值Num为3,如果大于等于200小
于300返回2,大于等于100小于200返回1,不到100字节返回0
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
fp=fopen("C:/User/fil.txt","r");
if(fp==NULL){
//直接错误退出
return -1;
}
//读文件
int num;
char buf[128]="";
num=fread(buf,5,4,fp);
printf("buf=%s\n",buf);
printf("num=%d\n",num);
return 0;
}
写文件
函数的定义:
size_t_fwrite(void *ptr,size_t_size,size_t_nmemb,FILE *stream);
函数的说明:
fwrite函数将ptr指向的内存里的数据,向stream所标志的文件中写入数据,一块是size字节,共nmemb块。
返回值:实际写入的块数
#include <stdio.h>
typedef struct{
int a;
int b;
char c;
}MSG;
int main(int argc, char *argv[])
{
FILE *fp;
fp=fopen("C:/User/fil.txt","r");
if(fp==NULL){
//直接错误退出
return -1;
}
//写文件:使用fwirte向文件写入一个结构体,因为fwrite的第一个参数是void类型的指针,
//所以不光可以写文件,还可以写结构体等类型,只要符合即可
MSG msg[4]={
1,2,'a',3,4,'b',5,6,'c',7,8,'d'
};
fwrite(msg,sizeof(msg),4,fp);
//注意:读写代码都写在这里的话,则读写共用同一个偏移量
//如果写文件结束起始偏移量在最后这时候想读文件需要重置偏移量
//将文件的偏移量设置为文件的起始位置
rewind(fp);
MSG rcv[4];
fread(rcv,sizeof(MSG),4,fp);
int i;
for(i =0;i<4;i++){
printf("%d -%d -%d\n",rcv[i].a,rcv[i].b,rcv[i].c);
}
return 0;
}
格式化读写文件
函数调用:
fprintf(文件指针,格式字符串,输出表列);
fscanf(文件指针,格式字符串,输出表列);
函数功能:从磁盘文件中读入或输出字符
fprintf和printf函数类似:
printf是将数据输出到屏幕上(标准输出)
fprintf函数是将数据输出到文件指针所指定的文件中
fscanf和scanf函数类似:
scanf是从键盘(标准输入)获取输入
fscanf是从文件指针所标示的文件中获取输入
char ch1='a';
int num1=50;
fprintf(fp,"%c %d\n",ch1,num1);
rewind(fp);
fscanf(fp,"%c %d\n",&ch1,&num1);
随机读写
上面的所有读写都是顺序读写,从头开始;但是实际经常需要读取某一部分内容,为了解决这个问题,可以移动文件内部的位置指针到需要读写的位子,再进行读写,这种被称为随机读写。实现随机读写的关键是要按要求移动位置指针,这被称为文件的定位
完成文件定位的函数:
rewind、fseek函数
1. rewind复位读写位置
rewind函数
void rewind(文件指针);
把文件内部的位置指针移动到文件首部
2. ftell测文件读写位置距文件开始有多少个字节
long ftell(文件指针);
取地文件流目前的读写位置
返回值:返回当前读写位置(距离文件起始的字节数),出错时返回-1
3. int fseek(FILE * stream,long offset,int whence);
//int fseek(文件类型指针,位移量,起始点);
函数功能:
移动文件流的读写位置
参数:
whence起始位置
文件开头 SEEK_SET 0
文件当前位置 SEEK_CUR 1
文件末尾 SEEK_END 2
位移量:以起始点为基点,向前、后移动的字节数,正数往文件末尾方向移动,负数反之
fseek(fp,50,SEEK_SET);