C语言之文件操作的总结

目录

一、什么是文件

二、为什么使用文件

三、文件的命名

四、文件结构与文件类型指针

1.文件结构

2.文件类型指针

五、文件的打开和关闭

1.打开文件

2.关闭文件

六、文件的顺序读写

1.fgetc()

2.fputc()

3.fgets()

4.fputc()

5.fscanf()与fprintf()

6.fread()与fwrite()

对比两组函数:

 七、文件随机读写

1.fseek()

2.ftell()

3.rewind()

八、其他函数

1.文件末尾检测函数feof()

2.读写错误检查函数ferror()

3.出错标记清除函数clearerr()

九、文本文件和二进制文件

 十、文件缓冲区


一、什么是文件

在操作系统中,文件是指驻留在外部介质(如磁盘等)中的一个有序数据集, 可分为程序文件数据文件(从文件功能的角度来分类的)。

(1)程序文件: 包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。 

(2)数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。数据文件还可分为各种类型,如:文本文件,图像文件,声音文件......

二、为什么使用文件

使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。

三、文件的命名

一个文件要有一个唯一的文件标识,以便用户识别和引用。 为了方便起见,文件标识常被称为 文件名。文件名包含3部分:文件路径 +文件名主干+文件后缀 。如: c:\code\test.txt

四、文件结构与文件类型指针

1.文件结构

FILE是C语言为了具体实现对文件操作而定义的一个包含文件操作相关信息的结构类型。FILE是用typedef重命名的,在头文件<stdio.h>中定义,因此,使用文件的程序都需要#include <stdio.h>

//FILE文件类型的说明
typedef struct
{
    short level;              //缓冲区使用量
    unsigned flags;           //文件状态标志
    char fd;                  //文件描述符
    short bsize;              //缓冲区大小
    unsigned char *buffer;    //文件缓冲区的首地址
    unsigned char *curp;      //指向文件缓冲区的工作指针
    unsigned char hold;       //其他信息
    unsigned istemp;          
    short token;              
}FILE;
每当打开一个文件的时候,系统会根据文件的情况自动创建一个 FILE 结构的变量,并填充其中的信息,使用者不必关心细节。

2.文件类型指针

(1)文件缓冲区是内存中用于数据存储的数据块,在文件处理过程中,程序需要访问该缓冲区实现数据的存储。文件缓冲区由系统自动分配,并不想数组那样通过数组名如下标来定位。为此,C语言引进FILE文件结构,其成员指针指向文件缓冲区,通过移动指针实现对文件的操作。也就是说,通过文件指针变量能够找到与它关联的文件

(2)FILE* fp;  //FILE是文件类型定义符,fp是文件类型的指针变量

(3)文件指针不像以前普通指针那样进行能进行fp++或*fp等操作,fp++将意味着指向下一个FILE结构。文件操作具有顺序性的特点,前一个数据取出后,下一次将顺序取后一个数据,fp->curp会发生变化,但改变是隐含在文件读写操作中的,而不需要在编程时写上fp->curp++。

五、文件的打开和关闭

1.打开文件

(1)打开文件功能用于建立系统与要操作的某个文件之间的关联,指定这个文件名并请求系统分配相应的文件缓冲区内存单元。打开文件由标准函数fopen()实现,其一般调用形式为:

FILE *fopen( const char *filename, const char *mode );

filename:文件名      mode:文件打开方式

该函数有返回值。如果执行成功,函数将返回包含文件缓冲区等信息的FILE结构地址,赋给文件指针fp。否则,返回一个NULL(空值)的FILE指针。

文件打开方式用来确定对打开的文件将进行什么操作。下表为C语言所有的文件打开方式。

文本文件二进制文件
使用方式含义如果指定文件不存在使用方式含义如果指定文件不存在
"r"打开文本文件进行只读出错"rb"打开二进制文件进行只读出错
"w"建立新文本文件进行只写
建立一个新的文件
"wb"建立二进制文件进行只写
建立一个新的文件
"a"打开文本文件进行追加
建立一个新的文件
"ab"打开二进制文件进行追加出错
"r+"打开文本文件进行读/写出错"rb+"打开二进制文件进行读/写出错
"w+"建立新文本文件进行读/写
建立一个新的文件
"wb+"建立二进制文件进行读/写
建立一个新的文件
"a+"打开文本文件进行读/写/追加
建立一个新的文件
"ab+"打开二进制文件进行读/写/追加
建立一个新的文件

 执行标准函数fopen(),计算机完成下述步骤的工作:

①在磁盘中找到指定文件

②在内存中分配保存一个FILE类型结构的单元(16B)

③在内存中分配文件缓冲区单元(512B)

④返回FILE结构地址(会送给fp)

(2)文件打开的实质是把磁盘文件与文件缓冲区对应起来,这样后面的文件读写操作只需使用文件指针即可。为保证文件操作的可靠性,我们再调用fopen()时可以做一个判断,以保证文件正常打开后进行读写。如:

#include <stdio.h>
int main()
{
    FILE* fp = fopen("data.text", "r");

	if (fp == NULL)
	{
		perror("fopen");
		exit(0);
	}
    ......
}

exit(0)是系统标准函数,作用是关闭所有打开的文件,并终止程序的执行。参数0表示程序正常结束,非0表示不正常的程序结束。

(3)C语言允许同时打开多个文件,不同文件采用不同文件指针指示,但不允许同一个文件在关闭前被再次打开

2.关闭文件

当文件操作完成后,应及时关闭它以防止不正常的操作。对于缓冲文件系统来说,文件的操作是通过缓冲区进行的。如果把数据写入文件,首先是写到文件缓冲区里,只有当写满512B,才会系统真正写入磁盘扇区。如果写的数据不到512B,发生程序异常终止,那么这些缓冲区中的数据将会被丢失。当文件操作结束时,即使未写满512B,通过文件关闭,能强制把缓冲区中的数据写入磁盘扇区,确保写文件的正常完成。除此之外,关闭文件还将释放文件缓冲区单元和FILE结构,使文件指针与具体文件脱钩。关闭文件通过调用标准函数fclose()实现。其一般格式为:

int fclose( FILE *stream );

stream:文件指针

该函数将返回一个整数,若该数为0表示正常关闭文件,否则表示无法正常关闭文件,所以关闭文件也应该使用条件判断,如:

if(fclose(fp))
{
    printf("Can not close the file!\n");
    exit(0);
}

六、文件的顺序读写

功能函数名适用于
字符方式文件读入函数fgetc()
所有输入流
字符方式文件写出函数fputc()
所有输出流
字符串方式文件读入函数fgets()
所有输入流
字符串方式文件写出函数fputs()
所有输出流
格式化方式文件读 入函数
fscanf()
所有输入流
格式化方式文件写出函数
fprintf()
所有输出流
数据块方式文件读入函数
fread()
文件
数据块方式文件写出函数
fwrite()
文件

注意:

(1)读入:数据从文件---->内存

         写出:数据从内存---->文件

(2) 流:一个高度抽象的概念,关注于如何写入各种外部设备,如:文件,屏幕,网络,光盘,软盘

c语言程序,就默认打开了三种流:标准输出流stdout,标准输入流stdin,标准错误流stderr,它们的类型都是FILE*

1.fgetc()

int fgetc( FILE *stream );

ch = fgetc(fp);  //从fp所指示的磁盘文件读入一个字符到ch。函数返回值若读文件成功为ch,否则EOF(符号常量,其值为-1)。

2.fputc()

 int fputc( int c, FILE *stream );

fputc(ch,fp);  //把一个字符ch写到fp所指示的磁盘文件上 。函数返回值若读文件成功为ch,否则EOF

在文件读写的过程中,fgetc()和fputc()实际上是对文件缓冲区进行读写,其工作过程与字符数组相似,存储单元由fp->curp指示。但文件操作很重要的一点是fp->curp会随fgetc()和fputc()的执行而自动改变,即

fputc(ch,fp);   等价于   (fp->curp) = ch;    fp->curp++;

文件的特点之一就是文件数据长度可以不确定,只要外存空间足够,数据可以不受限制地写入文件中。

3.fgets()

 char *fgets( char *string, int n, FILE *stream );

string:可以是字符数组名或字符指针(指向字符串的指针)

n:指定读入的字符个数

stream:文件指针

该函数如果执行成功,返回读取的字符串,如果失败,则返回空指针,这时string的内容就不确定了

函数调用时,最多读取n-1个字符,并将读入的字符串存入指针string所指向内存地址开始的n-1个连续的内存单元中。当函数读取的字符达到指定的个数,或接收到换行符,或接收到文件结束标志EOF时,将在读取的字符后面自动添加一个‘\0’字符;若有换行符,则将换行符保留(换行符在'\0'之前);若有EOF,则不保留EOF。

4.fputc()

int fputs( const char *string, FILE *stream );

string:要写入的字符串,可以是字符数组名、字符型指针变量或字符串常量

stream:文件指针

若函数执行成功,则返回所写的最后一个字符,否则,函数返回EOF

该函数把string写入文件时,字符串string的结束符'\0'不写入文件

5.fscanf()与fprintf()

(1)int fscanf( FILE *stream, const char *format [, argument ]... );

fscanf(文件指针,格式字符串,输入表)  

如:FILE* fp;     int n;        fp=fopen("a.txt","r");            fscanf(fp,"%d",&n);//从文件a.txt读入整型数到变量n

(2)int fprintf( FILE *stream, const char *format [, argument ]...);

fprintf(文件指针,格式字符串,输出表)

如:FILE* fp;     int n;        fp=fopen("b.txt","w");            fprint(fp,"%d",n);//把变量n的数值写入文件b.txt

文件a.txt和文件b.txt是以文本方式打开的,但读写操作的数据并不是字符类型,变量n的数据在内存中是以二进制的形式存储的,两者间的不同由系统自动处理。

6.fread()与fwrite()

size_t fread( void *buffer, size_t size, size_t count, FILE *stream );//buffer:存放输入数据的首地址

size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );//buffer:存放输出数据的首地址

void*:因为你不知道读写的数据是什么类型

size:数据块的字节数

count:要读写的数据块数

stream:文件指针

如:fread(da,4,5,fp);//从fp所指的文件中,每次读4个字节(一个实数)送入实数组fa中,连续读5次,即读5个实数到fa中。

注意:程序中用于输入的二进制文件无法用“记事本”等工具建立,它一般是其他程序或软件的处理结果。同样,作为程序结果的二进制文件也无法用“记事本”等工具查看

二进制文本文件的读写效率比文本文件要高,因为它不必把数据与字符做转换。另外,二进制文件更安全。

对比两组函数:

(1)scanf/fscanf/sscanf:
scanf:从标准输入(键盘)读取格式化的数据
fscanf:从所有的输入流读取格式化的数据
sscanf:从字符串中读取一个格式化的数据(把一个字符串转换为格式化数据)
int sscanf( const char *buffer, const char *format [, argument ] ... );
 
(2)printf/fprintf/sprintf

printf:打印格式化的数据输出到标准输出上(屏幕)
fprintf:把格式化的数据输出到所有输出流上
sprintf:把格式化的数据转化为一个字符串。int sprintf( char *buffer, const char *format [, argument] ... );

 七、文件随机读写

1.fseek()

int fseek( FILE *stream, long offset, int origin );

stream:文件指针

offset:移动偏移量。它是long类型,使用常量时,应加上后缀L,oddset可以为负值,表示按相反方向计算偏移量,即为正时表示从当前向后计算,为负时从当前位置向前计算

origin:从哪个位置开始计算偏移量。位置可取3种值:文件首部、当前位置和文件尾部,实际表示时分别对应值0、1、2,或常量SEEK_SET、SEEK_CUR、SEEK_END

该函数根据文件指针的位置和偏移量来定位文件指针

如:fseek(fp,20L,0);//表示将文件位置指针移动到离文件首20字节处

fseek(fp,-20L,SEEK_END);//表示将文件位置指针移动到离文件尾部前20字节处

2.ftell()

long ftell( FILE *stream );

stream:文件指针(已经定义过的)

返回文件指针相对于起始位置的偏移量,此函数出错时,返回-1L。

3.rewind()

void rewind( FILE *stream );

stream:文件指针

让文件指针的位置回到文件的起始位置

八、其他函数

1.文件末尾检测函数feof()

int feof( FILE *stream );

stream:文件指针

该函数成功返回1表示已经到了文件结束位置,0表示文件未结束。

注意在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。 而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束

(1)文本文件读取是否结束,判断返回值是否为 EOF fgetc ),或者 NULL fgets ),如:
fgetc 判断是否为 EOF
②fgets 判断返回值是否为 NULL
(2)二进制文件的读取结束判断,判断返回值是否小于实际要读的个数,如:
fread 判断返回值是否小于实际要读的个数
#include <stdio.h>
#include <stdlib.h>
int main(void) 
{
    int c;
    FILE* fp = fopen("test.txt", "r");
    if(!fp) 
    {
        perror("File opening failed");
        return EXIT_FAILURE;
    }
    while ((c = fgetc(fp)) != EOF) // //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
    { 
       putchar(c);
    }
    if (ferror(fp)) //判断是什么原因结束的
        puts("I/O error when reading");
    else if (feof(fp))
        puts("End of file reached successfully");
    fclose(fp);

    return 0;
}

2.读写错误检查函数ferror()

int ferror( FILE *stream );

stream:文件指针(必须是已经定义过的)

若返回值为0,表示未出错,否则表示出错

3.出错标记清除函数clearerr()

void clearerr( FILE *stream );

stream:文件指针(必须是已经定义过的)

该函数用来清除出错标志和文件结束标志,使它们为0

九、文本文件和二进制文件

1.根据数据的组织形式,数据文件被称为 文本文件 或者 二进制文件 。 数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件 。 如果要求在外存上以ASCII 码的形式存储,则需要在存储前转换。以 ASCII 字符的形式存储的文件就是 本文件
2.一个数据在内存中的存储:
(1)字符一律以 ASCII 形式存储
(2)数值型数据既可以用 ASCII 形式存储,也可以使用二进制形式存储
       如:整数10000, 如果以 ASCII 码的形式输出到磁盘,则磁盘中占用 5 个字节(每个字符一个字节),而 二进制形式输出,则在磁                 盘上只占 4 个字节

 十、文件缓冲区

ANSIC 标准采用 缓冲文件系统 处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“ 文件缓冲区 。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C 编译系统决定的。
#include <windows.h>
#include <stdio.h>
int main()
{
	FILE* pf = fopen("C:\\Users\\86159\\Desktop\\doc.study.txt", "w");

	fputs("abcdef", pf);//将代码放在缓冲区上
	printf("睡眠10s,已经写入数据,打开doc.study.txt文件,发现文件没有内容\n");
	Sleep(10000);
	printf("刷新缓冲区\n");
	fflush(pf);//刷新缓冲区,之后将缓冲区的数据写到文件中(磁盘);高版本vs上不能使用fflush
	printf("再睡眠10s,再次打开doc.study.txt文件,发现文件有内容\n");
	Sleep(10000);
	fclose(pf);//fclose时也会刷新缓冲区
	pf = NULL;

	return 0;
}

由此可以得到结论:

因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。 如果不做,可能导致读写文件的问题。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值