Unix下C语言----标准文件编程

来源:互联网 发布:虾米音乐网络异常 编辑:程序博客网 时间:2024/06/06 10:07

  在Unix的应用中,读写文件是最常见的任务。诸如报表的生成,日志的记录,批交易报文的传送都采用文件实现。标准文件编程就是操作文件最简单的huguidong11@yahoo.cn 工具。

 

1:文件的创建、打开、关闭与删除

   在标准库中,结构FILE是指向文件的指针,所有对文件的操作都是通过FILE完成的,FILE指针也称为文件流,它定义在头文件<stdio.h>,相对于整形的低级文件I/O描述符,它提供了I/O缓冲功能。

1)创建、打开、关闭与删除文件的函数族

标准文件编程库中用于文件创建、打开、关闭与删除的函数如下:

函数fopen打开或创建文件;fclose关闭文件;函数freopen重新打开文件;函数remove删除磁盘文件;函数rename更改文件名称。

ex1:以只读方式打开文本文件/etc/passwd:

FILE *fp;

fp=fopen("/etc/passwd","r");

ex2:以二进制方式创建文件rr.txt:

FILE *fp;

fp=fopen("rr.txt","wb");

 

2)freopen函数

  本函数实现文件流的替换。它首先关闭原文件流stream,然后再以freopen的方式打开一个新的文件流,此后对原文件流的任意操作都自动转换为对新文件流的操作。成功时返回指向新文件的FILE型指针,否则返回NULL。

  Unix进程默认打开三个文件:标准输出、标准输入、标准错误输出,它们的FILE标识符号分别是stdout、stdin、stderr。函数freopen常用于将以上三个文件流重定向,实现方法如下:

 

 

3)fclose函数

   为了减少系统资源消耗、避免误改文件内容和更新文件缓冲,应该及时关闭在将来一段时间内不需要使用的文件。函数fclose关闭文件流stream,成功时返回0,否则返回EOF;

4)remove

   函数remove删除字符串filename指定的文件或目录,当filename指定文件时,remove相当于unlinke函数,当filename指定目录时,相当于rmdir;

 

 

 

 1)字符读写

    字符读写函数每次只操作一个字符,为了提高磁盘读写效率,标准文件编程中提供了缓冲处理。

    (1)字符输入函数

    

     函数getc以unsigned char类型读取文件输入流stream中的一个字符,并将该无符号字符转化为整数返回,同时移动文件指针到下一个字符处。函数getchar实际上是关于getc的一个宏定义"getc(stdin)".

     函数fgetc的功能类似于getc,不同的是,它的执行速度远低于getc,因此getc常常被定义在宏中使用。

     当文件结束或错误时,这三个函数都将返回EOF,EOF为常数,因此正确的文件结束判断代码如下:

    

     (2)字符输出函数族

      

       函数putc首先先将int型参数c自动转换为unsigned char类型,然后写入文件流stream中,同时移动文件指针到下一个字符处。函数putchar 实际上是关于putc的宏定义"putc(stdout)"。

      (3)实例

      报文解析是Unix应用的一个重要内容。双方把约定的几个域通过某种排序和分割方式组合在一起,就成了报文。报文解析就是从报文中分解出各个域的数据,比如从银行的代收代付报文查找出账号域和资金域的内容。

      字符串报文是报文的重要分支,它以字符串为载体记录了域的数据。在字符串报文中,域与域之间最常见的2中分隔方式是固定长度分割和特殊字符(串)分割。前者每个域占用固定宽度,解析时只是读取特定位置的数据即可。后者的域与域之间由固定的字符(串)连接,解析时需要计算固定字符(串)出现的次数,以决定域的序号和内容。Unix中passwd文件就是由":"分割的字符串报文组合而成的。

     例子:一个使用字符读写函数解析报文的,程序读取文件的"/etc/passwd"中每一个字符,并将"用户名称"域(报文的第一个域)单独提出,存入文件"copyname.txt"中,源程序如下:

    

    

 2)按行读写

   标准函数编程库提供了行读写函数,该类函数读取一行以换行符"/n"结束的数据,写入数据时自动输出换行符。

   (1)行输入函数族

      

       函数gets从标准输入流(stdin)中读取一串字符存储到参数s所指向的内存空间中,文件结束或者错误发生时返回NULL,否则将返回参数s所指向的内存地址。

       函数fgets中键入了防溢出控制,它从文件流stream中读取一串字符到参数s所指向的内存空间,但读取数据的长度(包括换行符"/n")不能超过n-1,。参数n代表了字符串s的最大存储空间。倘若待读入的实际数据长度包括("/n")超过了n-1,函数将截取该n个字符返回,剩余的字符将在下一次fgets调用时读入。

       两个函数都把读取的字符信息存入字符串s中,并且自动增加字符串结束符"0",这也是fgets一次性最多只能读入n-1个字符的原因(第n个字符需要存储结束符"0")。函数调用成功时返回参数s的值,即指向输入的字符信息,否则返回空指针NULL。

     

     (2)行输出函数

     标准文件编程库中用于文件行输出的函数如下:

    

     参数s指向一串以字符串结束符"0"结尾的字符;函数puts把该字符串(不包括结束符"0")写入到标准输出流stdout中,并自动输出换行符"/n";函数fputs字符串s(不包括结束符"0")写入文件流stream中,但不再输出换行符"/n"。

     两函数都不输出字符串末的结束符,输出失败时,都返EOF。

     (3)实例:

      解析报文文件的另一种方法是先读入一行数据,再通过字符串函数分解。本处设计了一个使用行读写解析报文的例子,程序按行读取文件"/etc/passwd",并将"用户名称"域提取出来(报文的第一个域)单独提取出来,存入文件"copyname.txt"中,源程序如下:

  

 3)按块读写

    块读写函数,能够输入输出任何数量的字符,在操作二进制文件时常常使用,标准文件编程库中用于文件块输入输出的函数如下:

  

   函数fread从文件流stream中读入nitems个数据项存储到指针ptr所指向的内存中,每个数据项具有size字节大小,一次操作总共读入size*nitems个字节。

  函数fwrite将ptr的数据写入到stream中,每次可写入size*nitems个字符。参数nitems表示写入文件的数据项个数,参数size表示每个数据项具有的字节大小。

 注:在大多数Unix中,size_t被定义为无符号整形:

 typedef unsigned int size_t;

  fread&fwrite都不返回实际读写的字符个数,而返回的是实际读写的数据项数。成功时,返回值等于参数nitems值,否则返回值将小于nitems值。

  块I/O函数常应用于二进制文件读写中,操作比较灵活,既可以读写一个字符,也可以读写一个结构,还可以读写任意数据类型。

 

实例

     块读写函数经常操作二进制文件,保留内存信息或永久存储数据信息等。比如在软件中涉及一个文件型数据库,用以存取历史交易明细。

    本处设计了一个存取数据信息的实例,通过文件"array.dat"存储和读取一个整形数组,其中文件起始记录数组的长度,随后是数组的数据信息。源文件如下:

  

 


 

 

 

 

3:函数的变长参数

 

  文件的格式化参数都支持变长参数。定义时,变长参数列表通过省略号"..."表示,因此,具有变长参数列表的函数定义格式如下:

  type 函数名(参数1,参数2,参数n,...);

其中type为函数的返回值类型,参数1~n为定长参数,...代表变长参数,...必须定义在参数的最右端。如下例:

  int printf(const char * format,...);

  int mysum(...);

 

1)变长参数的使用

  Unix的变长参数通过va_list对象实现,定义在文件"stdarg.h"中,变长参数的应用模板代码如下:

 

1 step. va_list pvar

申明va_list数据类型变量pvar,该变量访问变长参数列表中的参数

2 step. va_start(pvar,parmN)

宏va_start初始化变长参数列表。pvar是va_list型变量,在step1定义,记载列表中的参数信息。parmN是省略号"..."前的一个参数名,va_start根据此参数,判断参数列表的起始位置。

3 step: va_arg(pvar,type)

获取变长参数列表中参数的值。pvar是step定义的va_list型变量,type为参数值的类型,也是红va_arg返回数值的类型,如:

va_arg(pvar,int);

va_arg(pvar,float);

宏va_arg执行完毕后自动更改对象pvar,将其指向下一个参数。

4 step:va_end(pvar)

关闭本次对变长参数列表的访问。

设计函数mysum,计算输入参数的和并返回结果,源程序如下:

 

compile & run

 

$make mysum

     cc -O -o mysum mysum.c

$./mysum

sum(1,4)=4;

sum(2,4,8)=12;

 

4)变长参数的传递

变长参数传递的函数族如下:

这些函数完全等价于格式化函数,只是在形式上采用固定参数替代变长参数列表,这样描述的函数更加紧凑,这些函数长应用于变长参数内部的功能实现。

实现:设计函数"int PrintLog(FILE *stream,const  char *pformat,...)",它按照字符串format的内容,控制后即参数的数量和格式,并在文件流stream中输出。源程序代码如下:

 

 

 


 

4:文件读写位置的定位

  在实际应用中,我们常常只需要读取文件中的一小段内容,或者写入一小段呢日哦难怪到文件中,使用文件的读写定位功能可以避免些不必要的的数据段。例如某文件由一系列固定大小记录块组成,当访问其第n块记录时,可以先将文件指针移动到第n块记录的起始位置,再访问1个记录快大小的数据,并不需要从文件头开始读取n个记录块。

  标准文件变成库中用于定位文件读写为止的函数如下:

 

其中fseek改变文件流stream中的访问位置,参数whence表示文件定位的方式,fseek中提供了三种定位方式,参数offset表明了定位的偏移量,其值可正可负,与whence一起确定访问文件的最终定位。

whence              含义                     文件定位于

SEEK_SET           从文件头开始定位   0+offset

SEEK_CUR          从当前位置开始定位  当前位置+offset

SEEK_END          从文件尾开始定位     文件末+offset

 

rewind重置流stream,将文件流定位于文件开始处,相当于执行了以下操作:

void fseek(stream,0L,SEEK_SET);

函数ftell获取文件流的当前位置,调用成功时将该值返回,否则返回-1。

注:

    大多数系统中,可以通过以下代码获取文件的长度(len)

   

 实例:

      本处设计了一个文件读写定位的例子,代码如下,其中seekwrite将整数值narray写入文件第"rec*sizeof(int)"处;函数seekread从文件的第"rec*sizeof(int)"位置中读取整数值存入narray.

     如果定位超过了文件的最大长度且执行了写入操作,文件长度将延长到当前位置,中间部分自动补0;


5:文件的状态

  每一个流对象内部都保持了两个指示状态:错误指示状态和文件结束状态,函数ferror和feof分别检查这两个状态,函数strerror显示错误的提示信息。

 

(1)文件的错误与结束状态

     

    当文件I/O发生错误时,调用ferror函数将返回非0值,否则返回0值。当文件结束时,调用feof函数返回非0值,否则返回0值,函数clearerr清除文件错误标志和EOF标志。

   实例:

   读取文件"/etc/passwd",当文件结束时自动退出。

    

 

(2)文件的错误信息

    错误状态指示器仅能判断错误是否发生,不能明确错误的内容,标准文件编程库用于明确错误信息的函数如下:

    extern int errno;

    #include<string.h>

    char *strerror(int errnum);

 

  其中外部变量errno代表了发生错误的代码,函数strerror获取第errnum号错误的详细信息。

 

 


 

6:文件的缓冲

   所谓文件写缓冲,是指文件流在执行输出操作时,并不立刻将数据写入文件,而是先把数据累计到缓冲区,再以块为单位批量输出到文件中,同理,文件读缓冲是指文件流在执行输入操作时,以块为单位读取文件内容,多余的数据存储在内存中。如果下次读操作的内容刚好在同一块中,则可以直接返回结果,避免一次输入操作。通过缓冲技术,可以减少低级I/O函数read和write函数的调用次数,从而大大提高软件执行效率。

  1)缓冲模式

   标准文件编程库采用FILE类型描述文件流,与低级I/O函数相比,最大的特性就是应用及增加了缓冲功能(低级I/O函数只使用了文件系统自带的缓冲功能),文件的输入输出以"缓冲块"为单位批量完成,并且根据"缓冲块"大小,提供了三种缓冲模式。

   (1)全缓冲(_IOFBF):一般读写普通磁盘文件采用全缓冲模式。

   (2)行缓冲(_IOLBF):比如调用fgets函数从标准输入流stdin中输入字符,当且仅当客户输入回车换行时,函数才返回。

   (3)无缓冲(_IONBF):比如stderr采用无缓冲模式;

  

 2)缓冲函数

  

   setbuf设置文件流stream的缓冲区,参数buf指向一个大小为BUFSIZ的内存块,调用成功后,文件流stream使用该内存块作为新的缓冲区。倘若buf是空指针NULL,文件流stream的缓冲将被完全关闭。缓冲区内存块的定义一般为:

   char buf[BUFSIZ]; ---其中BUFSIZ是stdio.h中的常数,代表缓冲区的大小,常为256的整数倍。

   setvbuf设置了文件流stream的缓冲区和缓冲模式,缓冲模式由参数type确定.

   任何时候,都可以使用fflush刷新缓冲区,并将缓冲区的内容强制输出到文件中,参数stream指明了更新的 文件流,当其值为NULL时,系统将刷新全部文件流的缓冲区。

  实例:

  本处设计了一个缓冲显示的实例:

 

 

 

 

 

 


 

7:项目:通用函数库之调试功能库封装

   1)调试库内容:

      格式化日志输出

      十六进制日志输出

      信息判断

   2)调试库设计

     (1)PrintLog

      格式化日志输出函数,其原型为:

      int PrintLog(FILE *fp,const char *pformat,...);

     

   

     (2)PrintTraceLog

        int PrintTraceLog(const char *pformat,...);

        核心代码

       

 

  (3) Verify

       信息判断函数,其原型为:

      int Verify(int bStatus,const char *szBuf,const char *szFile,int nLine);

     

 

  (5)调试库应用实例:

  

  

 

 

 

 

 

 

 

 

 

 

 

 

 



2:文件的无格式读写

  标准程序编程对文件流的输入输出操作,能以无格式方式读写,也可以实现有格式读写,无格式读写,分为字符读写、按行读写、按块读写;


原创粉丝点击