主要文件操作包括以下函数:
打开文件 | 关闭所有打开的文件并结束程序 | 从文件指针指向的文件流中读取一个字符 | 将指定字符写到文件指针所指向的当前位置上 | 关闭文件流释放文件指针 |
---|---|---|---|---|
fopen() | exit() | getc() | putc() | fclose() |
格式化输出到一个文件中 | 从一个流中执行格式化输入 | 从指定文件流中读取数据,每次读取一行 | 向指定的文件写入一个字符串 | 重设文件流的读写位置为文件开头 |
fprintf() | fscanf() | fgets() | fputs() | rewind() |
移动文件流的读写位置 | 取得文件流的读取位置 | 更新缓冲区 | 获取流的当前文件位置 | 将文件指针定位在指定的位置 |
fseek() | ftell() | fflush() | fgetpos() | fsetpos() |
判断文件指针到达文件尾 | 判断文件指针是否读写错误 | 把一个无符号字符推入指定流中 | 把缓冲区与流相关 | 从流中读取数据 |
feof() | ferror() | ungetc() | setvbuf() | fread() |
将数据写至文件流 | 清除文件流的错误旗标 | 输入和输出的位置重新定向 | 设置文件流的缓冲区 | 设置文件流为线性缓冲区 |
fwrite() | clearerr() | freopen() | setbuffer() | setlinebuf() |
文件(file):通常是指存储在外部介质上的一段已命名的存储区。(数据集合)
磁盘文件:(我们通常认识的文件)
文件用于储存程序、文档、数据、表格、照片、视频和许多其他种类的信息
可以实现创建、写入、读取等操作
设备文件:
键盘 屏幕:标准输入输出文件
其它设备 : 打印机等
补充:磁盘文件分类(文件模式-在逻辑上): 文本模式 与 二进制模式
*-------------- 在物理上所有文件的内容都以二进制形式(0或1)储存
·文本文件:
基于字符编码储存的,常见编码有ASCII码,UNICODE等
·二进制文件:
基于值编码,自己根据具体应用,制定某个值的意义
两者比较:
文本文件 | 二进制文件 | |
---|---|---|
编码 | 基于字符 (译码容易) | 二进制 (译码困难) |
空间利用率 | 任何符号占一个字节 (低) | 使用位(bit)来处理数据 (高) |
可读性 | 文本编辑工具 (高) | 具体的文件解码器 (低) |
1.虽然C提供了二进制模式和文本模式,但是这两种模式的实现可以相同。
(C是作为开发UNIX的工具而创建的)
2.UNIX使用同一种文件格式处理文本文件和二进制文件的内容,这两种模式对于UNIX、Linux实现而言完全相同。
总结:C把文件看作是一系列连续的字节,每个字节都能被单独读取
1.C语言中不能直接操作文件,只能采用调用库函数间接对文件进行操作
2.C语言操作文件前要调用"打开函数"将文件打开, fopen()打开文件后会得到一个文件指针fp
- 调用各种有关函数,利用fp对文件进行具体操作(读或写)
- 在文件使用完毕时,及时调用关闭函数来关闭文件
- C语言中所有的文件操作都围绕文件指针完成
定义文件指针的一般形式为
FILE * 指针变量标识符;
FILE * fp = NULL;//定义文件指针并使其初始化空值
C语言中有三个特殊的文件指针无需定义、打开可以直接使用:
C程序会自动打开3个’文件’,它们被称为
在默认情况下,标准输入是系统的普通输入设备,通常为键盘;
标准输出和标准错误输出是系统的普通输出设备,通常为显示屏。
打开文件,该函数声明在stdio.h中
语法
FILE *fopen( const char *fname, const char *mode );
第1个参数是待打开文件的名称,更确切地说是一个包含该文件名的字符串地址。
第2个参数是一个字符串,指定待打开文件的模式。
成功打开文件后,fopen()将返回文件指针 ; 如果发生错误, fopen()返回NULL
打开文件的模式
备注:
1.在UNIX和Linux这样只有一种文件类型的系统下,带b字母(二进制)的模式和不带b字母(文本)的模式相同.
2.打开模式如果为"w",若文件存在,则清空文件内容,进行写操作.
语法:
#include
void exit( int exit_code );
例:
exit(0);
↑和return 0;的作用相同^(也有不同之处,见备注)
通常0值表示程序正常退出,非零值表示错误返回
备注:
1.C标准也要求了 0 或 宏EXIT_SUCCESS 用于表明成功结束程序,
宏EXIT_FAILURE用于表明结束程序失败。
2.在最初调用的main()中使用return与调用exit()的效果相同,
但其他情况下有两点不同之处:
即使在其他函数中(除main()以外)调用exit()也能结束整个程序
如果main()在一个递归程序中,exit()仍然会终止程序,但是return只会把控制权交给上一级递归,直至最初的一级。
都位于stdio.h头文件内
getc()
语法:
int getc( FILE *stream );
ch = fgetc(fp);
↑从fp指定的文件中获取一个字符 getc()和fgetc()是一样的
参数是文件指针,获取并返回下一个字符,如果到达文件尾返回EOF
putc()
语法:
int putc(int ch , FILE *stream );
putc(ch, fpout);
↑把字符ch放入FILE指针fpout指定的文件中
第1个参数是待写入的字符,第2个参数是文件指针,返回值为写入的字符, 发生错误时返回EOF
(函数执行完成后,文件指针继续向后移动)
备注:
1.putc(ch, stdout)与putchar(ch)的作用相同
getc(stdin) 与 getchar() 的作用相同
2.getc()为了避免读到空文件,应该使用入口条件循环↓
while (( ch = getc(fp)) != EOF)//判断读到的是否为文件尾
{
putchar(ch); //处理输入
}
语法:
int fclose( FILE *stream );
fclose(fp);
↑关闭fp指定的文件,释放已关联到流的所有缓冲区
备注:
1.对于较正式的程序,应该检查是否成功关闭文件。
如果成功关闭,fclose()函数返回0
否则返回*EOF
*是一个系统常量,其值被定义为-1,且只适用于文本文件,二进制文件中需要feof()来确定是否到了文件结束部分。
2.如果磁盘已满、移动硬盘被移除或出现I/O错误,都会导致调用fclose()函数失败。
都位于stdio.h头文件内
fprintf()
语法:
int fprintf( FILE *stream, const char *format, … );
fprintf( out, "Hello %s\n", name );
↑将“ ”内的内容写入out指向的文件内
与printf()的区别是fprintf()需要用第1个参数指定待处理的文件
fscanf()
语法:
int fscanf(FILE *stream, char *format[,argument…]);
fscanf(out, "%d", &i);
↑从out指向的文件内“ ”内读取i
与scanf()的区别是fscanf()需要用第1个参数指定待处理的文件
备注:
1.fprintf()和 fscanf()的工作方式与 printf()和 scanf()类似。但是,与putc()不同的是,fprintf()和fscanf()函数都把FILE指针作为第1个参数,而不是最后一个参数。
2.fprintf()把数值转换为字符数据,这种转换可能会改变值
例:
num=0.33333输出0.33,储存为4个字符(“0”, “.” , “3” ,“3”),再次读取精度降低
为保证数值在储存前后一致,最精确的做法是使用与计算机相同的位组合来储存
(后方学习的fwrite()和fread())
都位于stdio.h头文件内
fgets()
语法:
char *fgets( char *str, int num, FILE *stream );
fgets(a, N, fp);
↑a是char类型数组的名称,N是想要读取字符串的长度+1,fp是文件指针
从给出的文件流中读取[N- 1]个字符并且把它们转储到a(字符串)中
. 第1个参数和gets()函数一样,表示储存输入位置的地址(char * 类型);
. 第2个参数是一个整数,表示待输入字符串的大小;
. 最后一个参数是文件指针,指定待读取的文件。
备注:
1.fgets()函数的结束方式有三种
2.fgets()成功时返回str(字符串),失败时(遇到EOF)返回NULL.
fputs()
语法:
int fputs( const char *str, FILE *stream );
fputs(a, fp);
↑将a(字符串)内的内容写入fp指向的文件内
第1个是字符串的地址;
第2个是文件指针。
成功时返回非负值, 失败时返回EOF
备注:
1.和 puts()函数不同,fputs()在打印字符串时不会在其末尾添加换行符
( 因为fgets()函数保留了换行符,与其对应的fputs就不会再添加了 )
都位于stdio.h头文件内
fseek()
语法:
int fseek( FILE *stream, long offset, int origin );
fseek(fp, 0L, SEEK_END);/* 定位到文件末尾 */
fseek(fp, -count, SEEK_END); /* 回退 */
↑
fseek()的第1个参数是FILE指针,指向待查找的文件;
fseek()的第2个参数是偏移量(offset)。该参数表示从起始点开始要移动的距离。该参数必须是一个long类型的值,可以为正(前移)、负(后移)或0(保持不动)。
fseek()的第3个参数是模式,该参数确定起始点。根据ANSI标准,在stdio.h头文件中规定了几个表示模式的明示常量↓
模式 | 偏移量的起始点 | 数字表示 |
---|---|---|
SEEK_SET | 从文件的开始处开始 | 0 |
SEEK_CUR | 从当前位置开始 | 1 |
SEEK_END | 从文件的结束处开始 | 2 |
可以使用数值0、1、2分别表示这3种模式
下面是一些示例
L后缀表明其值是long类型。
fseek(fp, 0L, SEEK_SET); // 定位至文件开始处
fseek(fp, 10L, 0); // 定位至文件中的第10个字节
fseek(fp, 2L, 1); // 从文件当前位置前移2个字节
fseek(fp, 0L, 2); // 定位至文件结尾
fseek(fp, -10L, SEEK_END); // 从文件结尾处回退10个字节
备注:
1.fseek()成功时返回0,失败时返回-1,并设置 errno 的值,可以用 perror 函数来输出错误信息
(fseek()不会返回当前读写位置,所以需配合ftell函数 ).
2.fseek( )一般用于二进制文件,当然也可以用于文本文件.
3.fseek( ) 用于文本操作时一定要注意回车换行的情况 , 因为一般情况下会将回车视为两个字符 \r (0x0D)、\n (0x0A),但真实的文件读写和定位是按照一个字符进行处理(遇到这种情况,可以采用整个读入内存,然后再在内存中手工插入0x0D的方法)。
4.你可以使用fseek()移动超过一个文件,但是不能在开始处之前. 使用fseek()清除关联到流的EOF标记.
ftell()
语法:
long ftell( FILE *fp );
ftell(fp);
↑得到文件位置指针当前位置fp相对于文件首的偏移字节数
ftell()函数的返回类型是long,它返回的是当前的位置。
在随机方式存取文件时,由于文件位置频繁的前后移动,程序不容易确定文件的当前位置。
所以需要函数 ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数。
备注:
1.对2.1G以上的文件(大文件)操作时可能出错
2.ftell() 也能方便地知道一个文件的长度(利用两个函数配合)
fseek(fp, 0L,SEEK_END); //将文件指针移动到文件尾
len =ftell(fp);//获得文件指针的位置
首先将文件的当前位置移到文件的末尾,然后调用函数ftell()获得当前位置相对于文件首的位移,该位移值等于文件所含字节数。
3.在随机方式存取文件时,由于文件位置频繁前后移动,程序不容易确定文件的当前位置。在使用 fseek 函数后,再调用函数 ftell 就能非常容易地确定文件的当前位置。如下面的示例代码所示:
long getfilelength(FILE *fp)
{
long curpos=0L;
long length=0L;
curpos = ftell(fp);
fseek(fp, 0L, SEEK_END);
length = ftell(fp);
fseek(fp, curpos, SEEK_SET);
return length;
}
两个处理较大文件的新定位函数,不使用long类型表示位置,而是用fpos_t类型(非基本类型)
fgetpos()
语法:
int fgetpos(FILE *stream, fpos_t * restrict pos);
fgetpos(stream, &filepos);
↑取得当前文件的指针所指的位置,并把该指针所指的位置数存放到pos所指的对象中
第一个参数是文件指针
第二个参数表示了文件的指定位置
如果成功,fgetpos()函数返回0;如果失败,返回非0并设置errno
fsetpos()
语法:
int fgetpos(FILE *stream,const fpos_t *pos);
fsetpos( fp, &pos );
↑将文件指针定位在pos指定的位置上
第一个参数是文件指针
第二个参数表示了文件的指定位置
如果成功,fsetpos()函数返回0;如果失败,则返回非0。
fpos_t类型的值应通过之前调用fgetpos()获得
备注:
1.pos值以内部格式存储,仅由fgetpos()和fsetpos()使用.
语法:
int ungetc(char c, FILE *stream);
ungetc(ch, stdin);
↑把一个字符退回到输入流中
第1个参数是字符;第2个参数是文件指针。每次只会放回一个字符
如果成功,则返回被推入的字符,否则返回 EOF,且流 stream 保持不变。
备注:
1.如果实现允许把一行中的多个字符放回输入流,那么下一次输入函数读入的字符顺序与放回时的顺序相反。
语法:
int fflush(FILE *stream);
fflush( stdout );
↑刷新缓冲区
第1个参数是是文件指针。
如果成功,返回0值,否则返回 EOF,且流设置错误标识符(feof)
备注:
1.如果文件指针为空,则所有输出缓冲区都被刷新
2.在输入流中使用fflush()函数的效果是未定义的。只要最近一次操作不是输入操作,就可以用该函数来更新流(任何读写模式)
语法:
int setvbuf(FILE *stream, char *buf, int type, unsigned size);
setvbuf(stdout, buff, _IOFBF, 1024);
↑程序把缓冲输出保存到 buff
第1个参数是指针fp识别待处理的流
第2个参数buf指向待使用的存储区:如果buf的值不是NULL,则必须创建一个缓冲区。
第3个参数是指定了文件缓冲的模式
如果成功,则该函数返回 0,否则返回非零值。
模式 | 描述 |
---|---|
_IOFBF | 全缓冲:对于输出,数据在缓冲填满时被一次性写入。对于输入,缓冲会在请求输入且缓冲为空时被填充。 |
_IOLBF | 行缓冲:对于输出,数据在遇到换行符或者在缓冲填满时被写入,具体视情况而定。对于输入,缓冲会在请求输入且缓冲为空时被填充,直到遇到下一个换行符。 |
_IONBF | 无缓冲:不使用缓冲。每个 I/O 操作都被即时写入。buffer 和 size 参数被忽略。 |
fread()
语法:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread(buffer, strlen(c)+1, 1, fp);
↑读取fp文件中一个strlen( c )长度的字符串并将地址赋值给buffer
第1个参数是指针,要与你需要读取的数据类型相对应
第2个参数是读取的每个元素的大小,以字节为单位
第3个参数是元素的个数
第4个参数是文件指针
成功读取的元素总数会以 size_t 对象返回,size_t 对象是一个整型数据类型。正常情况下,该返回值就是nmemb,但如果出现读取错误或读到文件结尾,该返回值就会比nmemb小。
fwrite()
语法:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
fwrite(str, sizeof(str) , 1, fp );
↑向p文件中写入一个strlen(str)长度的str字符串
第1个参数是指针,要与你需要写入的数据类型对应
第2个参数是读取的每个元素的大小,以字节为单位
第3个参数是元素的个数
第4个参数是文件指针
如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。正常情况下,该返回值就是nmemb,但如果出现写入错误,返回值会比nmemb小。
备注:
1.size_t是根据标准C类型定义的类型,它是sizeof运算符返回的类型,通常是unsigned int,但是实现可以选择使用其他类型
2.在ANSI C函数原型中,第一个实际参数都被转换成指向void的指针类
型,这种指针可作为一种通用类型指针
如果标准输入函数返回EOF,通常便是函数已到达文件结尾(使用feof())或是读取错误(使用ferror())
feof()
语法:
int feof(FILE *stream)
feof(fp);
↑测试fp是否到达文件尾
参数是文件指针
当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。
ferror()
语法:
int ferror(FILE *stream)
ferror(fp) ;
↑ 测定读写错误
参数是文件指针
如果设置了与流关联的错误标识符,该函数返回一个非零值,否则返回一个零值。
1.要访问文件,必须创建文件指针(类型是FILE *)并把指针与特定文件名相关联。
2.如果要在不损失精度的前提下保存或恢复数值数据,请使用二进制模式以及fread()和fwrite()函数。
3.如果打算保存文本信息并创建能在普通文本编辑器查看的文本,请使用文本模式和函数(如getc()和fprintf())。
4.fopen()函数为标准 I/O 打开一个文件,并创建一个用于存储文件和缓冲区信息的结构。返回指向该结构的指针。
5.feof()和ferror()函数报告I/O操作失败的原因
6.fread()函数,C把输入看作是二进制值并将其储存在指定存储位置。如果使用fscanf()、getc()、fgets()或其他相关函数,C则将每个字节看作是字符码。然后fscanf()和scanf()函数尝试把字符码翻译成转换说明指定的其他类型。
7.getc()和 fgetc()系列函数把输入作为字符码储存,将其作为单独的字符保存在字符变量中或作为字符串储存在字符数组中。
8.fwrite()将二进制数据直接放入输出流,而其他输出函数把非字符数
据转换成用字符表示后才将其放入输出流。
9.ANSI C提供两种文件打开模式:二进制和文本。以二进制模式打开文件时,可以逐字节读取文件;以文本模式打开文件时,会把文件内容从文本的系统表示法映射为C表示法。对于UNIX和Linux系统,这两种模式完全相同。
10.通常,输入函数getc()、fgets()、fscanf()和fread()都从文件开始处按顺序读取文件。然而, fseek()和ftell()函数让程序可以随机访问文件中的任意位置。fgetpos()和fsetpos()把类似的功能扩展至更大的文件。与文本模式相比,二进制模式更容易进行随机访问。
文章顺序与部分内容引用**《C Primer Plus 第6版 中文版》**