【C】C语言中文件操做相关内容

 

1. 文件和流的关系

  C将每一个文件简单地做为顺序字节流。每一个文件用文件结束符结束,或者在特定字节数的地方结束,这个特定的字节数能够存储在系统维护的管理数据结构中。当打开文件时,就创建了和文件的关系。html

  在开始执行程序的时候,将自动打开3个文件和相关的流:标准输入流、标准输出流和标准错误。流提供了文件和程序的通讯通道。打开一个文件将返回指向FILE结构(在stdio.h中定义)的指针,它包含用于处理文件的信息,也就是说,这个结构包含文件描述符。文件描述符是操做系统数组(打开文件列表的索引)。每一个数组元素包含一个文件控制块(FCB, File Control Block),操做系统用它来管理特定的文件。程序员

  标准输入、标准输出和标准错误是用文件指针stdin、stdout和stderr来处理的。数组

  C语言把磁盘文件当作是字符(或字节)的序列,按照存储信息的形式来讲,文件主要是有文本文件和二进制文件。文本文件由一个个字符组成,每一个字节存放一个ASCII码制,表明一个字符。二进制文件把内存中的数据按其在内存中的存储形式原样放入磁盘空间。数据结构

二进制文件以及文本文件均可以看作是“数据流”。app

2. C语言文件管理的实现

  C程序用不一样的FILE结构管理每一个文件。程序员可使用文件,但不须要知道FILE结构的细节。实际上,FILE结构是间接地操做系统的文件控制块(FCB)来实现对文件的操做的。例如FILE结构体中的_file实际上就是一个文件描述符,做为进入打开文件表索引的整数。函数

3. 操做系统文件管理简介

  文件是存放在物理磁盘上的,包括文件控制块(FCB)和数据块。文件控制块一般包括文件权限、日期(建立、读取、修改)、拥有者、文件大小、数据块信息。数据块用来存储实际的内容。对于打开的文件,操做系统是这样管理的:编码

1spa

系统维护了两张表,一张是系统级打开文件表,一张是进程级打开文件表(每一个进程有一个)。操作系统

  系统级打开文件表复制了文件控制块的信息等;进程级打开文件表保存了指向系统级文件表的指针及其余信息。指针

  系统级文件表每一项都保存一个计数器,即该文件打开的次数。咱们初次打开一个文件时,系统首先查看该文件是否已在系统级文件表中,若是不在,则建立该项信息,不然,计数器加1。当咱们关闭一个文件时,相应的计数也会减1,当减到0时,系统将系统级文件表中的项删除。

  进程打开一个文件时,会在进程级文件表中添加一项。每项的信息包括当前文件偏移量(读写文件的位置)、存取权限、和一个指向系统级文件表中对应文件项的指针。系统级文件表中的每一项经过文件描述符(一个非负整数)来标识。

  FILE结构体中的_file成员应该是指向进程级打开文件表,而后,经过进程级打开文件表能够找到系统级打开文件表,进而能够经过FCB操做物理磁盘上面的文件。

  每打开一次文件,哪怕屡次打开的都是同一个文件,进程级打开文件表中应该都会添加一个记录。若是是打开的是同一个文件,这多条记录对应着同一个物理磁盘文件。因为每一次打开文件所进行的操做都是经过进程级打开文件表中不一样的记录来实现的,这样,至关于每次打开文件的操做是相对独立的。

4. 缓冲区

  当咱们从键盘输入数据的时候,数据并非直接被咱们获得,而是放在了缓冲区中,而后咱们从缓冲区中获得咱们想要的数据 。若是咱们经过setbuf()setvbuf()函数将缓冲区设置10个字节的大小,而咱们从键盘输入了20个字节大小的数据,这样咱们输入的前10个数据会放在缓冲区中,由于咱们设置的缓冲区的大小只可以装下10个字节大小的数据,装不下20个字节大小的数据。那么剩下的那10个字节大小的数据怎么办呢?暂时放在了输入流中。请看下图:

 

  上面的箭头表示的区域就至关是一个输入流,红色的地方至关于一个开关,这个开关能够控制往深绿色区域(标注的是缓冲区)里放进去的数据,输入20个字节的数据只往缓冲区中放进去了10个字节,剩下的10个字节的数据就被停留在了输入流里!等待下去往缓冲区中放入!接下来系统是如何来控制这个缓冲区呢?
再说一下 FILE 结构体中几个相关成员的含义:
     cnt  // 剩余的字符,若是是输入缓冲区,那么就表示缓冲区中还有多少个字符未被读取
     ptr  // 下一个要被读取的字符的地址
     base  // 缓冲区基地址
  在上面咱们向缓冲区中放入了10个字节大小的数据,FILE结构体中的 cnt 变为了10 ,说明此时缓冲区中有10个字节大小的数据能够读,同时咱们假设缓冲区的基地址也就是 base 是0x00428e60 ,它是不变的 ,而此时 ptr 的值也为0x00428e60 ,表示从0x00428e60这个位置开始读取数据,当咱们从缓冲区中读取5个数据的时候,cnt 变为了5 ,表示缓冲区还有5个数据能够读,ptr 则变为了0x0042e865表示下次应该从这个位置开始读取缓冲区中的数据 ,若是接下来咱们再读取5个数据的时候,cnt 则变为了0 ,表示缓冲区中已经没有任何数据了,ptr 变为了0x0042869表示下次应该从这个位置开始从缓冲区中读取数据,可是此时缓冲区中已经没有任何数据了,因此要将输入流中的剩下的那10个数据放进来,这样缓冲区中又有了10个数据,此时 cnt 变为了10 ,注意了刚才咱们讲到 ptr 的值是0x00428e69 ,而当缓冲区中从新放进来数据的时候这个 ptr 的值变为了0x00428e60 ,这是由于当缓冲区中没有任何数据的时候要将 ptr 这个值进行一下刷新,使其指向缓冲区的基地址也就是0x0042e860这个值!由于下次要从这个位置开始读取数据!
  在这里有点须要说明:当咱们从键盘输入字符串的时候须要敲一下回车键才可以将这个字符串送入到缓冲区中,那么敲入的这个回车键(\r)会被转换为一个换行符\n,这个换行符\n也会被存储在缓冲区中而且被当成一个字符来计算!好比咱们在键盘上敲下了123456这个字符串,而后敲一下回车键(\r)将这个字符串送入了缓冲区中,那么此时缓冲区中的字节个数是7 ,而不是6。
  缓冲区的刷新就是将指针 ptr 变为缓冲区的基地址 ,同时 cnt 的值变为0 ,由于缓冲区刷新后里面是没有数据的! 

5. 缓冲区操做(设置、清除)

  (1).清除文件缓冲区函数: int fflush(FILE *stream); int flushall(); fflush()函数将清除由stream指向的文件缓冲区里的内容,经常使用于写完一些数据后,当即用该函数清除缓冲区,以避免误操做时,破坏原来的数据。 flushall()将清除全部打开文件所对应的文件缓冲区。

  (2).设置文件缓冲区函数 void setbuf(FILE *stream,char *buf); void setvbuf(FILE *stream,char *buf,int type,unsigned size); 这两个函数将使得打开文件后,用户可创建本身的文件缓冲区,而不使用fopen()函数打开文件设定的默认缓冲区。 

  程序输出有两种方式:一种是即时处理方式,另外一种是先暂存起来,而后再大块写入的方式,前者每每形成较高的系统负担。所以,c语言实现一般都容许程序员进行实际的写操做以前控制产生的输出数据量。这种控制能力通常是经过库函数setbuf实现的。若是buf是一个大小适当的字符数组,那么:setbuf(stdout,buf);语句将通知输入/输出库,全部写入到stdout的输出都应该使用buf做为输出缓冲区,直到buf缓冲区被填满或者程序员直接调用fflush(译注:对于由写操做打开的文件,调用fflush将致使输出缓冲区的内容被实际地写入该文件),buf缓冲区中的内容才实际写入到stdout中。缓冲区的大小由系统头文件<stdio.h>中的BUFSIZ定义。

  函数setvbuf()用来设定文件流的缓冲区,其原型为:int setvbuf(FILE * stream, char * buf, int type, unsigned size);【参数】stream为文件流指针,buf为缓冲区首地址,type为缓冲区类型,size为缓冲区内字节的数量。
  参数类型type说明以下:

    _IOFBF (满缓冲):当缓冲区为空时,从流读入数据。或当缓冲区满时,向流写入数据。

    _IOLBF (行缓冲):每次从流中读入一行数据或向流中写入—行数据。

    _IONBF (无缓冲):直接从流中读入数据或直接向流中写入数据,而没有缓冲区。

  【返回值】成功返回0,失败返回非0。 

  setbuf()和setvbuf()函数的实际意义在于:用户打开一个文件后,能够创建本身的文件缓冲区,而没必要使用fopen()函数打开文件时设定的默认缓冲区。这样就可让用户本身来控制缓冲区,包括改变缓冲区大小、定时刷新缓冲区、改变缓冲区类型、删除流中默认的缓冲区、为不带缓冲区的流开辟缓冲区等。

  说明:在打开文件流后,读取内容以前,调用setvbuf()能够用来设置文件流的缓冲区。

  文件的随机读写函数

  前面介绍的文件的字符/字符串读写,均是进行文件的顺序读写,即老是从文件的开头开始进行读写。这显然不能知足咱们的要求,C语言提供了移动文件指针和随机读写的函数,它们是:移动文件指针函数: long ftell(FILE *stream); int rewind(FILE *stream); fseek(FILE *stream,long offset,int origin); 函数ftell()用来获得文件指针离文件开头的偏移量。当返回值是-1时表示出错。rewind()函数用于文件指针移到文件的开头,当移动成功时,返回0,不然返回一个非0值。fseek()函数用于把文件指针以origin为起点移动offset个字节,其中origin指出的位置可有如下几种: origin 数值 表明的具体位置 SEEK_SET 0 文件开头 SEEK_CUR 1 文件指针当前位置 SEEK_END 2 文件尾 例如: fseek(fp,10L,0); 把文件指针从文件开头移到第10字节处,因为offset参数要求是长整型数,故其数后带L。 fseek(fp,-15L,2); 把文件指针从文件尾向前移动15字节。

6. 文件操做相关函数

1、打开文件操做:FILE *fopen(char *filename, char *mode) 
      filename: 采用绝对或相对路径的目标文件名 
      mode: 文件的类型和操做要求 
      返回值: 目标文件指针或空指针值NULL(打开异常时)

文件类型 t (text): 文本文件(可省略不写);   b (banary): 二进制文件

      从文件编码的方式来看,文件可分为ASCII码文件和二进制码文件两种。

      ASCII文件也称为文本文件,这种文件在磁盘中存放时每一个字符对应一个字节,用于存放对应的ASCII码。ASCII码文件可在屏幕上按字符显示。二进制文件是按二进制的编码方式来存放文件的。
      二进制文件虽然也可在屏幕上显示,但其内容没法读懂。
      C系统在处理这些文件时,并不区分类型,都当作是字符流,按字节进行处理。输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。 所以也把这种文件称做流式文件。把一个文本文件读入内存时,要将ASCII码转换成二进制码, 而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,所以文本文件的读写要花费较多的转换时间。对二进制文件的读写不存在这种转换。

文件操做类型:r (read): 读【目标文件必须存在,不然报错】
                      w (write): 写【目标不存在时自动建立】
                      a (append): 追加【目标文件必须存在,不然报错】
                      + : 读和写

操做类型组合方式

                                                                     操     做     说      明                                                             

"rt"

只读打开一个文本文件,只容许读数据

"wt"

只写打开或创建一个文本文件,只容许写数据

"at"

追加打开一个文本文件,并在文件末尾写数据

"rb"

只读打开一个二进制文件,只容许读数据

"wb"

只写打开或创建一个二进制文件,只容许写数据

"ab"

追加打开一个二进制文件,并在文件末尾写数据

"rt+"

读写打开一个文本文件,容许读和写

"wt+"

读写打开或创建一个文本文件,容许读写

"at+"

读写打开一个文本文件,容许读,或在文件末追加数据

"rb+"

读写打开一个二进制文件,容许读和写

"wb+"

读写打开或创建一个二进制文件,容许读和写

"ab+"

读写打开一个二进制文件,容许读,或在文件末追加数据

2) 凡用“r”打开一个文件时,该文件必须已经存在,且只能从该文件读出。
3) “w”打开的文件只能向该文件写入。若打开的文件不存在,则以指定的文件名创建该文件,若打开的文件已经存在,则将该文件删去,重建一个新文件。
4) 若要向一个已存在的文件追加新的信息,只能用“a”方式打开文件。但此时该文件必须是存在的,不然将会出错。
5) 在打开一个文件时,若是出错,fopen将返回一个空指针值NULL。在程序中能够用这一信息来判别是否完成打开文件的工做,并做相应的处理。

2、关闭文件操做:int fclose(FILE *fp)
      fp 待关闭文件的文件指针。返回值: 0(正常关闭),非0(关闭异常)

3、 读字符函数: int fgetc(FILE *fp) 
      fp 待读文件的文件指针。返回值: 读出字符的ASCII码或EOF(文件结束时)

      fgetc函数的功能是从指定的文件中读一个字符。在文件内部有一个位置指针。用来指向文件的当前读写字节。在文件打开时,该指针老是指向文件的第一个字节。使用fgetc 函数后, 该位置指针将向后移动一个字节。文件结束时,该指针指向EOF, 所以可连续屡次使用fgetc函数,读取多个字符直至遇到EOF为止。 应注意文件指针和文件内部的位置指针不是一回事。文件指针是指向整个文件的,须在程序中定义说明,只要不从新赋值,文件指针的值是不变的。文件内部的位置指针用以指示文件内部的当前读写位置,每读写一次,该指针均向后移动,它不需在程序中定义说明,而是由系统自动设置的。

  对于fgetc函数的使用有如下几点说明:1) fgetc函数调用中,读取的文件必须是以读或读写方式打开的。2) 读取字符的结果也能够不向字符变量赋值, 例如: fgetc(fp); 可是读出的字符不能保存。3) 在文件内部有一个位置指针。用来指向文件的当前读写字节。在文件打开时,该指针老是指向文件的第一个字节。使用fgetc 函数后,该位置指针将向后移动一个字节。所以可连续屡次使用fgetc函数,读取多个字符。

4、 写字符函数: int fputc(int ch, file *fp)
      ch 待写入文件的字符的ASCII。fp 待写文件的文件指针。返回值: 如写入成功则返回写入的字符, 不然返回EOF

  putc函数的使用也要说明几点:1) 被写入的文件能够用写、读写、追加方式打开,用写或读写方式写入字符从文件首开始。如需保留原有文件内容,但愿写入的字符被写入的文件若不存在,则建立该文件。2) 每写入一个字符,文件内部位置指针向后移动一个字节。3) fputc函数有一个返回值,如写入成功则返回写入的字符,不然返回一个EOF。可用此来判断写符,写入一个文件,再把该文件内容读出显示在屏幕上。

5、读字符串函数:char *fgets(char *str, int num, FILE *fp)
      str 保存从文件读取出来的字符串。num 表示从文件中读出的字符串不超过 n-1个字符。在读入的最后一个字符后加上串结束标志'/0'。fp 待读文件的文件指针。返回值: 字符数组的首地址或者NULL(当读到文件末尾或发生错误时返回)
      功能描述: 读字符串函数fgets函数的功能是从指定的文件中读一个字符串到字符数组中,如:fgets(str,n,fp)的意义是从fp所指的文件中读出n-1个字符送入字符数组str中。

      fgets函数有两点说明:1. 在读出n-1个字符以前,如遇到了换行符或EOF,则读出结束。2. fgets函数也有返回值,其返回值是字符数组的首地址。

6、写字符串函数: int fputs(char *str, file *fp)
      str 待写入文件的字符串。fp 待写文件的文件指针。返回值: 非负整数(成功),EOF(失败
      功能描述: fputs函数的功能是向指定的文件写入一个字符串

7、数据块读写函数: int fwrite(void *buf, int size, int count, FILE *fp) | int fread(void *buf, int size, int count, FILE *fp)
      buf fread函数中,它表示存放输入数据的首地址。在fwrite函数中,它表示存放输出数据的首地址。size 表示数据块的字节。count 表示要读写的数据块块数。fp表示文件指针 。返回: 已读取或已写入的数据块块数
8、 格式化读写函数 int fscanf(FILE *fp, char *format,…) | int fprintf(FILE *fp, char *format,…) 
      fscanf函数,fprintf函数与前面使用的scanfprintf 函数的功能类似,都是格式化读写函数。 二者的区别在于 fscanf 函数和fprintf函数的读写对象不是键盘和显示器,而是磁盘文件。

9、文件的随机读写 
      前面介绍的对文件的读写方式都是顺序读写, 即读写文件只能从头开始,顺序读写各个数据。 但在实际问题中常要求只读写文件中某一指定的部分。 为了解决这个问题可移动文件内部的位置指针到须要读写的位置,再进行读写,这种读写称为随机读写。 实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。文件定位移动文件内部位置指针的函数主要有两个, 即 rewind 函数和fseek函数。
  rewind函数前面已屡次使用过,其调用形式为: rewind(文件指针); 它的功能是把文件内部的位置指针移到文件首。 下面主要介绍fseek函数。
      fseek函数用来移动文件内部位置指针,其调用形式为: fseek(文件指针,位移量,起始点); 其中:文件指针指向被移动的文件。 位移量表示移动的字节数,要求位移量是long型数据,以便在文件长度大于64KB 时不会出错。当用常量表示位移量时,要求加后缀“L”起始点表示从何处开始计算位移量,规定的起始点有三种:文件首,当前位置和文件尾。其表示方法以下:

起始点    表示符号    数字表示
──────────────────────────
文件首    SEEK—SET    0
当前位置   SEEK—CUR     1
文件末尾   SEEK—END     2

10、文件检测函数

      C语言中经常使用的文件检测函数有如下几个。1、文件结束检测函数feof函数调用格式: feof(文件指针) 功能:判断文件是否处于文件结束位置,如文件结束,则返回值为1,不然为0。2、读写文件出错检测函数ferror函数调用格式: ferror(文件指针) 功能:检查文件在用各类输入输出函数进行读写时是否出错。 如ferror返回值为0表示未出错,不然表示有错。3、文件出错标志和文件结束标志置0函数clearerr函数调用格式: clearerr(文件指针); 功能:本函数用于清除出错标志和文件结束标志,使它们为0值。

相关文章
相关标签/搜索