本篇文章介绍C语言的文件操作,内容列表如下:
C标准使用fopen函数打开文件,该函数有两个参数,第一个参数是文件的绝对路径或者相对路径,第二个参数是文件的打开模式,返回文件指针FILE*
,如果打开失败,返回NULL
首先,FILE*不是指向实际的文件,FILE是一个定义在stdio里的一个结构体
,下面给出这个结构体的定义
typedef struct __sFILE {
unsigned char *_p; // 缓冲区的当前位置
int _r; // getc在当前缓冲区已读的数据
int _w; // putc在当前缓冲区剩余可写的数据
short _flags; // 标志位,如果为0,表示当前文件可用
short _file; // 仅用于Unix,表示文件描述符,否则值为-1
struct __sbuf _bf; // 缓冲区结构体数据
int _lbfsize; // 值为0或者-_bf._size, 用于内联 putc
// 传递给io函数的缓存指针
void *_cookie;
// close函数指针
int (* _Nullable _close)(void *);
// read函数指针
int (* _Nullable _read) (void *, char *, int);
// seek函数指针
fpos_t (* _Nullable _seek) (void *, fpos_t, int);
// write函数指针
int (* _Nullable _write)(void *, const char *, int);
struct __sbuf _ub; // ungetc缓冲区
struct __sFILEX *_extra;
int _ur; // 当使用ungetc方法向输入缓冲区写回字符时,保_r的值
// 可支持的最低的写回缓冲区
unsigned char _ubuf[3];
// 可支持的最低的读取缓冲区
unsigned char _nbuf[1];
// 供fgetln使用的缓冲区结构,当fgetln读取的数据正好部分在当前缓冲区时有用
struct __sbuf _lb; /* buffer for fgetln() */
// 根据seek值获取数据块的单元大小
int _blksize;
// seek的offset值
fpos_t _offset;
} FILE;
可以不用知道该结构体内变量的作用,我们基本也用不到,这都是给操作系统IO函数使用的
,我们只需要知道这个结构体包含了我们读写缓冲区的信息就行了
文件的打开模式其实基本就四种:
r
:只读模式打开w
:以写模式打开文件,并且将文件清空,如果文件不存在,创建一个a
:以写模式打开文件,不清空文件,定位到文件末尾,如果文件不存在,创建一个wx
:C11新增的文件打开模式,该模式下,如果文件存在,会打开失败
,也就是如果我以该模式成功创建并打开一个文件,我就不能再次用该模式打开这个文件了,因为该文件已经存在了,这也就是该模式文件具有的独占性
如果这四种文件打开模式后面添加+
号,就变成了读写模式,意思还是不变,只是既能写入,也能读出
如果这四种文件打开模式后面添加b
,就变成了二进制模式,意思还是不变,只是以二进制模式读写数据
二进制模式和文本模式的区别可以参考文章C语言文本模式和二进制模式
注意:+号和b可以一起添加并且顺序不重要
关闭当前文件,传递参数FILE*,如果关闭成功,返回0,否则返回EOF
这三个函数用来读取和写回字符
传递参数文件指针FILE*,返回从输入流获取到的字符
第一个参数传递写入的字符,第二个参数传递文件指针FILE*,向输出流添加字符
向输入流写回字符,第一个参数传递写回的字符,第二个参数传递文件指针FILE*,写回后使用getc会读取写回的字符
fprintf和printf功能类似,只不过第一个参数是一个文件指针FILE*,fprintf可以具有和printf一样的功能,只需要把文件指针写成标准文件输出stdout
即可
fscanf和scanf功能类似,只不过第一个参数是一个文件指针FILE*,fscanf可以具有和scanf一样的功能,只需要把文件指针写成标准文件输入stdin
即可
就一个参数,传递文件指针FILE*,返回文件开始
fgets传递三个参数,第一个参数是存储字符数据的数组指针,第二个参数是读取的最大字符,如果值为m,则最多读取m-1个,因为后面还要添加’\0’,第三个参数是文件指针FILE*,也可以是标准输入stdin
fputs传递两个参数,第一个参数传递输出字符数组的指针,第二个参数是文件指针FILE*,也可以是标准输出stdout
fputs不会在输出完成后添加换行符
fseek函数用于设置当前文件读写的位置,该函数接受三个参数,第一个参数是文件指针FILE*,第二个参数是相对于第三个参数设置的偏移量offset
,可正可负,第三个参数是设置文件的起始点mode
,有三个:
SEEK_SET
:文件开始处
SEEK_CUR
:当前文件位置
SEEK_END
:文件结尾
也就是当前文件的位置为:pos = mode + offset
如果fseek正常,返回0,如果seek失败,返回-1
ftell函数只需要一个参数,当前的文件指针FILE*,返回long类型,表示当前位置距离起始位置的字节大小。ftell在二进制模式和文本模式的不同可以参考文章C语言文本模式和二进制模式
fflush函数接受一个参数,就是文件指针FILE*
该函数用于输出缓冲区的刷新
,即将输出缓冲区的数据写入文件或者标准输出。如果文件指针为NULL,所有输出缓冲区都被刷新
该函数是一个自定义缓冲区的函数,该函数接受四个参数:
返回值是int类型,如果操作成功,返回0
缓冲区刷新模式有三种类型:
_IOFBF:(io full buffer flush),缓冲区满了以后刷新缓冲区
_IOLBF:(io line buffer flush),遇到换行符刷新缓冲区
_IONBF:(io no buffer flush),不刷新缓冲区
看下面的例子:
#include
int main(void)
{
char data[10];
FILE* file = fopen("test.d", "w");
if(file != NULL)
{
setvbuf(file, data,_IOFBF , sizeof(data));
fprintf(file, "t\nhis is a test!!!");
fclose(file);//在这行打断点
}
return 0;
}
当使用_IOFBF
模式时,在断点处,查看输出文件,发现只有下面这些数据:
t
his is a
因为输入的数据太多,缓冲区满了,所以缓冲区刷新,文件被写入一部分,剩下的内容没有填满缓冲区,所以不会输出到文件,等fclose之后就都写入文件了。
当使用_IOLBF
模式时,在断点处,查看输出文件,发现只有下面这些数据:
t
his is a t
在t后遇到换行符,刷新缓冲区,写入文件(换行符也写入了文件中
),然后继续读取数据到缓冲区,到test的t时缓冲区满,再次刷新缓冲区,所以文件显示如此
当使用_IONBF
模式时,数据压根就不会进缓冲区,直接写入文件
这两个函数是用来写入二进制文件的
fwrite有四个参数
返回结果表示实际写入的数据块数,如果写入正确,一般等于参数三
fread也有四个参数
返回结果表示实际读取的数据块数,如果读取正确,一般等于参数三
通常来说,使用这两个函数来读写二进制数据非常方便,但是这两个函数读取写入数据是完全是按照内存的数据布局来写入或者读取的
,这样如果我们在一个小端法存放数据的机器上保存的数据,如果在大端法存放数据的机器上读取可能出现问题,我们在后面的文章会实现一个机器无关的数据写入和读取库。
如果标准输入函数返回EOF时,我们通常认为到达文件结尾,但是如果读取错误时,也会返回EOF,那么怎么区分这两种情况呢。这两个函数就专门用来区分这两种情况的。
feof函数需要一个参数,就是文件指针FILE*,如果达到文件结尾,返回非零值,否则返回0
ferror函数也需要一个参数,就是文件指针FILE*,如果读取文件出现错误,返回非零值,否则返回0