C语言文件操作

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/wucz122140729/article/details/98435952

 

       文件是数据的集合,这个数据集的名称就是文件名。实际上在前面的各章中我们已经多次使用了文件,例如源程序文件、目标文件、可执行文件、库文件 (头文件)等。文件通常是存放在外部介质(如磁盘等)上的,在使用时才调入内存中来。从用户的角度看,Linux系统的文件分为普通文件和设备文件两种。

       普通文件是指存放在磁盘或其它外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序; 也可以是一组待处理的原始数据,或者是一组输出的结果。对于源文件、目标文件、 可执行程序可以称作程序文件,对输入输出数据可称作数据文件。

       设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等。在Linux操作系统中,把外部设备也看作是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。通常把显示器定义为标准输出文件,一般情况下在屏幕上显示有关信息就是向标准输出文件输出。如前面经常使用的printf函数就是这类输出。键盘通常被指定标准的输入文件,从键盘上输入就意味着从标准输入文件上输入数据。

       根据文件中数据的组织形式的不同,可以把文件分为文本文件和二进制文件。

文本文件和二进制文件

1、文本文件

       数据以字符组成,把每个字符的 ASCII 码值存入文件中。每个 ASCII 码值占一个字节,每个字节表示一个字符。所以文本文件也称作字符文件或 ASCII 文件。

2、二进制文件

       把数据对应的二进制数值存储到文件中,是字节序列文件。

       例如数据 123,如果按文本文件形式存储,把数据看成三个字符:'1'、'2'、'3' 的集合,文件中依次存储每个字符的 ASCII 码值,格式如下表所示。

字符

'1'

'2'

'3'

ASCII(十进制)

49

50

51

ASCII(二进制)

00110001

00110010

00110011

       如果按照二进制文件形式存储,数据 123 被看成字符、短整型、短整型、长整型,存储方式分别如下:

       字符型一个字节

              01111011

       短整型2个字节

              00000000 01111011

       整型4个字节

              00000000 00000000 00000000 01111011

       长整型8个字节

              00000000 00000000 00000000 00000000 00000000 00000000 00000000 01111011

       文本文件可以用vi和记事本打开,看到的都是ASCII字符,二进制文件用vi可以打开,但是看到的是乱码,没有意义。打开二进制文件之前,必须知道它的格式,一般来说,不同的二进制文件采用相应的软件打开它,例如图片文件用图片查看软件,音频文件用音乐播放器。

       文本文件可以换行,例如我们写的C程序,就是文本文件,有换行。

       二进制文件没有换行的说法,也没有字符串的说法,也没有以空字符结尾的说法,它是数据流。

打开文件和关闭

       C 语言中对任何文件进行操作,都必须先“打开”文件,操作完成后,再“关闭”文件。

1、文件指针

       打开文件的时候,C语言为打开的文件分配一个文件信息区,该信息区中包含文件描述信息、该文件所使用的缓冲区大小及缓冲区位置、该文件当前读写到的位置等基本信息。这些信息保存在一个结构体类型变量中(struct _IO_FILE),这个结构体有一个别名FILE(typedef struct _IO_FILE FILE),FILE结构体和对文件操作的库函数在 stdio.h 头文件中声明的。

       打开文件的时候,调用打开文件的函数fopen时会动态分配一个FILE结构体,并把FILE结构体地址作为函数的返回值,即文件指针。调用关闭文件的函数fclose时候,除了关闭文件,还会释放文件指针占用的内存空间。

2、打开文件

       我们可以使用 C语言提供的库函数fopen来创建一个新的文件或者打开一个已存的文件,调用fopen函数成功后,返回一个文件指针( FILE)。

       下面是这个函数调用的声明。

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

       参数filename 是字符串,表示需要打开的文件名,可以包含目录名,如果不包含路径就表示程序运行的目录。实际开发中,采用文件的全路径,即包含目录名。

       参数mode也是字符串,表示打开文件的模式,打开模式可以是下列值中的一个。

模式

含 义

说 明

rt

只读

文件必须存在,否则打开失败。

wt

只写

如果文件存在,则清除原文件内容;如果文件不存在,则新建文件。

at

追加只写

如果文件存在,则打开文件,如果文件不存在,则新建文件。

rt+

读写

文件必须存在。在只读 r 的基础上加 '+' 表示增加可写的功能。

wt+

读写

在只写w的模式上增加可读的功能。

at+

读写

在追加只写a的模式上增加可读的功能。

       如果处理的是二进制文件,则需使用下面的打开模式来取代上面的打开模式。

              "rb"、"wb"、"ab"、"rb+"、"wb+"、"ab+"

       如果打开的是文本文件,打开模式的字母t可以省略,rt可以写成r,打开二进制文件时,打开模式的字母b不能省略。

       英文单词:read简写r、text简写t、write简写w、append简写a、binary简写b。

       对于文件打开模式mode,如果理解不了,不要去死记硬背,以后用到的时候再研究。

3、关闭文件

       关闭文件的函数 fclose 的声明。

              int fclose(FILE *fp);

4、示例(book108.c)

       

       对初学者来说,以下代码可能难以理解。

              if ( (fp=fopen("book1.c","r")) == 0 )

       其实(fp=fopen("book1.c","r"))表达式的值就是fp,我在讲if分支语句的时候就讨论过了。我们可以用一段代码来测试一下。

       

       运行结果

       

       如果还不理解,就这么抄吧,抄多了就熟了。

5、注意事项

       1)调用fopen打开文件的时候,一定要判断返回值,如果文件不存在、或没有权限、或磁盘空间满了,都有可能造成打开文件失败。

       2)文件指针是调用fopen的时候,系统动态分配的内存,如果文件操作完了、或函数返回或程序退出的时候,必须用fclose关闭文件指针,释放内存,否则后果严重。

       3)如果文件指针是空的,用fclose关闭它相当于操作空指针,后果严重。

文本文件的读写

       在实际开发中,文本文件以行为单位存放字符串,如C程序的源代码,一段文字等,所以一般是按行写入或读取数据。

1、向文件中写入数据

       C语言向文件中写入数据库函数有fputc、fputs、fprintf,在实际开发中,fputc和fputs没什么用,只介绍fprintf就可以了。fprintf函数的声明如下:

              int fprintf(FILE *fp, const char *format, ...);

       fprintf函数的用法与printf相同,只是多了第一个参数文件指针,表示把数据输出到文件。

       一般情况下,程序员不必关心fprintf函数的返回值。

       示例(book111.c)

           

        编译book111.c程序并执行,采用cat命令查看/tmp/test1.txt的内容,如下:

       

 

       可以看到/tmp/test1.txt中有5行记录,不管执行多少次都是5行记录,因为文件打开的方式是w,每次打开文件的时候都会清空原文件中的记录。

       各位可以试一下把文件打开模式设置为a,看看程序执行的效果。

2、从文件中读取数据

       C语言从文件中读取数据的库函数有fgetc、fgets、fscanf,在实际开发中,fgetc和fscanf没什么用,只介绍fgets就可以了。fgets函数的声明如下:

              char *fgets(char *buf, int size, FILE *fp);

       fgets的功能是从文件中读取一行。

       参数buf是一个字符串,用于保存从文件中读到的数据。

       参数size是打算读取内容的长度。

       参数fp是待读取文件的文件指针。

       如果文件中将要读取的这一行的内容的长度小于size,fgets函数就读取一行,如果这一行的内容大于等于size,fgets函数就读取size-1字节的内容。

       调用fgets函数如果成功的读取到内容,函数返回buf,如果读取错误或文件已结束,返回空,即0。如果fgets返回空,可以认为是文件结束而不是发生了错误,因为发生错误的情况极少出现。

       示例(book113.c)

       

       运行结果

       

       需要重点说明的是,在读取到 size-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束。这就意味着,不管 size 的值多大,fgets() 最多只能读取一行数据,不能跨行。在实际开发中,可以将 size 的值设置地足够大,可以是10240,每次就可以读取到一行完整的数据。

二进制文件的读写

      二进制文件没有行的概念,存放的数据也不是字符串,不存在以0结尾的情况。

      我们直接把内存中的数据结构写入二进制文件,读取的时候,也是从文件中读取数据结构的大小一块数据,直接保存到数据结构中。

1、向文件中写入数据

       fwrite() 库函数用来向文件中写入块数据,它的原型为:

              size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

       参数的说明:

       ptr:为内存区块的指针,存放了要写入的数据的地址,它可以是数组、变量、结构体等。

       size:固定填1。

       nmemb:表示打算写入数据的字节数。

       fp:表示文件指针。

       函数的返回值是本次成功写入数据的字节数,一般情况下,程序员不必关心fwrite函数的返回值。

       示例(book115.c)

       

       编译并运行程序,得到数据文件,用vi命令打开文件,显示如下:

       

       可以看到很多乱码,其实并不是文件的内容乱,而是vi无法识别文件的格式,把内容当成ASCII码显示,文件中的字符串是ASCII码,所以能正确显示,但年龄和身高是整数,就无法显示了。

2、从文件中读取数据

       fread() 库函数用来从文件中读取块数据,它的原型为:

              size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp);

         ptr:用于存放从文件中读取数据的变量地址,它可以是数组、变量、结构体等。

       size:固定填1。

       nmemb:表示打算读取的数据的字节数。

       fp:表示文件指针。

       调用fread函数如果成功的读取到内容,函数返回读取到的内容的字节数,如果读取错误或文件已结束,返回空,即0。如果fread返回空,可以认为是文件结束而不是发生了错误,因为发生错误的情况极少出现。

       示例(book117.c)

       

       运行结果

       

3、注意事项

       1)我对fread和fwrite函数的size和nmemb以及它们的返回值的解释是不正确的,我这么做的原因是为了方便大家的使用,正确的解释会把大家搞晕,等你功力够的时候,我们再讨论它的准确含义。

       2)fwrite和fread函数也可以写入和读取文本文件,但是没有换行的概念,不管是0还是换行或其它的特殊字符,无区别对待。

       3)二进制文件有自已的数据格式,写入数据时要按约定的格式写,读取的时候也要按约定的格式读取,book115.c写入的是超女数据结构数据,book117.c就要用超女数据结构来存放读取的数据,这道理就像图片查看软件无法打开音频文件,音频播放软件也无法打开图片文件。

       4)如果程序员不知道二进制文件的格式,也可以用fread和fwrite函数读写文件,例如文件复制和文件传输程序,它不会去解析文件的数据,所以不必关心文件的格式。

       示例(book119.c)

       

       运行结果

       

文件定位

       在文件内部有一个位置指针,用来指向当前读写的位置,也就是读写到第几个字节。在文件打开时,如果打开模式是r和w,位置指针指向文件的第一个字节,如果打开模式是a,位置指针指向文件的尾部。每当从文件里读n个字节或文件里写入n个字节之后位置指针也会向后移动n个字节。

       文件位置指针与C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,也就是读写到第几个字节,它不表示地址。文件每读写一次,位置指针就会移动一次,它不需要你在程序中定义和赋值,而是由系统自动设置,对程序员来说是隐藏的。

       在实际开发中,偶尔需要移动位置指针,实现对指定位置数据的读写。我们把移动位置指针称为文件定位。

       C语言提供了ftell、rewind和fseek三个库函数来实现文件定位功能。

1、ftell函数

       ftell函数用来返回当前文件位置指针的值,这个值是当前位置相对于文件开始位置的字节数。它的声明如下:

              long ftell(FILE *fp);

2、rewind函数

       rewind函数用来将位置指针移动到文件开头,它的声明如下:

              void rewind ( FILE *fp );

3、fseek函数

       fseek() 用来将位置指针移动到任意位置,它的声明如下:

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

       参数说明:

       1)fp 为文件指针,也就是被移动的文件。

       2)offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。offset 为正时,向后移动;offset 为负时,向前移动。

       3)origin 为起始位置,也就是从何处开始计算偏移量。C语言规定的起始位置有三种,分别为:0-文件开头;1-当前位置;2-文件末尾。

              fseek(fp,100,0);     // 从文件的开始位置计算,向后移动100字节。

              fseek(fp,100,1);     // 从文件的当前位置计算,向后移动100字节。

              fseek(fp,-100,2);    // 从文件的尾部位置计算,向前移动100字节。

4、注意事项

       当offset是向文件尾方向偏移的时候,无论偏移量是否超出文件尾,fseek都是返回0,当偏移量没有超出文件尾的时候,文件指针式指向正常的偏移地址的,当偏移量超出文件尾的时候,文件指针是指向文件尾的。并不会返回偏移出错-1值。

       当offset是向文件头方向偏移的时候,如果offset没有超出文件头,是正常偏移,文件指针指向正确的偏移地址,fseek返回值为0,当offset超出文件头时,fseek返回出错-1值,文件指针还是处于原来的地址。

文件缓冲区

       在操作系统中,存在一个内存缓冲区,当调用fprintf、fwrite等函数往文件写入数据的时候,数据并不会立即写入磁盘文件,而是先写入缓冲区,等缓冲区的数据满了之后才写入文件。还有一种情况就是程序调用了fclose时也会把缓冲区的数据写入文件。

       在实际开发中,如果程序员想把缓冲区的数据立即写入文件,可以调用fflush库函数,它的声明如下:

              int fflush(FILE *fp);

       函数的参数只有一个,即文件指针,返回0成功,其它失败,程序员一般不关心它的返回值。

标准输入、标准输出和标准错误

       Linux操作系统为每个程序默认打开三个文件,即标准输入stdin、标准输出stdout和标准错误输出stderr,其中0就是stdin,表示输入流,指从键盘输入,1代表stdout,2代表stderr,1,2默认是显示器。

                printf("Hello world.\n");

         等同于

                fprintf(stdout,"Hello world.\n");

       这几个文件指针没什么用,让大家了解一下就行。在实际开发中,我们一般会关闭这几个文件指针。

课后作业

1、编写示例程序,从界面上输入五名超女的数据,存放在struct st_girl结构体数组中,然后把结构体数组以二进制的方式写入文件。

2、编写示例程序,把上一题写入的数据从二进制文件中读取出来,存入struct st_girl结构体中,然后在界面上显示出来。

3、编写示例程序,从界面上输入五名超女的数据,存放在struct st_girl结构体数组中,然后把结构体数组以xml字符串的方式写入文本文件。文件内容的格式如下:

       <name>美女1</name><age>20</age><height>166</height><sc>一般</sc><yz>漂亮</yz>

       <name>美女2</name><age>18</age><height>160</height><sc>火辣</sc><yz>一般</yz>

       <name>美女3</name><age>22</age><height>177</height><sc>一般</sc><yz>漂亮</yz>

       <name>美女10</name><age>26</age><height>159</height><sc>火辣</sc><yz>不行</yz>

4、编写示例程序,把上一题写入的数据从文本文件中读取出来,并解析xml,存入struct st_girl结构体中,然后在界面上显示出来。

5、文本文件和二进制文件存放数据的方式有区别,但是,在Linux平台下,打开文件的模式,文本文件模式和二进制文件模式没有区别。(在windows平台下,如果以“文本”方式打开文件,当读取文件的时候,系统会将所有的"/r/n"转换成"/n";当写入文件的时候,系统会将"/n"转换成"/r/n"写入。 如果以"二进制"方式打开文件,则读/写都不会进行这样的转换。)

       用fgets和fprintf可以以行的方式读写文本文件,但不能读写二进制文件。

       用fread和fwrite可以读写文本文件和二进制文件,但是没有行的说法。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值