两种I/O文件函数

  • fopen    ANSI 标准文件I/O,基于低层次I/O
  • open     低层次I/O 


   

ANSI I/O本质

  • DOS/WINDOWS平台,MS DOS 文件读写, 汇编语言编写
  • LINUX平台,unix/linux文件读写,C语言编写



  

文件类型FILE

包含一个指针


包含一个stream (

C语言把文件看成stream )



   

file open 
 
 r          Open text file for reading 
 
 r+         Open for reading and writing 
 
 w    Truncate file to zero length or create text file for writing 
 
 w+         The file is created if it does not exist, otherwise it is truncated 
 
 a      Open  for  appending (writing at end of file).   
 
 The stream is positioned at the end of the file.



   

fopen三种基本模式r,w,a只允许针对文本文



  

  C语言允许最大同时打开16个文件

实际只允许同时打开13个文件,因为有另外三个标准文件stdout,stdin,stderr




    

文件读写的基本语句

 1

#include <stdio.h>

FILE

 2


if((stream=fopen("testtmp","r+"))==NULL)

{ printf("open file error/n");

  return;

}

 3

int ret;

if ((ret=fclose(stream))==-1) printf("close file error/n");

  




  

  顺序读文件(按字符)

  

while((c=fgetc(stream)) 
 != EOF ) printf("%c",c);    每读一个字符, 
 文件指针会(自动)移动一格

顺序读文件(按字符串)-------从一个文件依次读一行,直到读到NULL

char record[100],*re; 
 
 while ((re=fgets(record, 
 100 ,stream))!= 
 NULL ) 
 
 printf("%s",record);


 读一行,以回车换行作为读一行的尾部标志




   

从文件中fgets的字符串是读一行,即包括了回车

所以printf("%s")即可,不用"%s/n",因为字符串里面带了回车了


这也是个漏洞,字符串里带了回车


所以非常不利于字符串比对strcmp   


   scanf读文件,文件指针不移动,需要手动fseek移指针


fseek(stream,3,SEEK_SET);
 fscanf(stream,"%c",&c[0]);
 fseek(stream,11,SEEK_SET);
 fscanf(stream,"%c",&c[1]);



  

文件读写到文件末尾

   

文件指针会指到NULL (而不是EOF,EOF不是地址,而是结尾字符

  •     fput(),fgetc()会返回EOF
  •     fgets()会返回NULL


   

linux和DOS对文件的结束判别是不同的

linux没有文件结束符 ,是以目录项的文件长度做为文件结束的判别手段


DOS看0x1a文件结束符




   

linux 和DOS的换行符的区别

 linux

  DOS

0x0a   即 10

即’/n’

即LF

 CR,LF双码


  


   

读文件时, 不能用unsigned char c作为返回值, 因为EOF不在unsigned范围内

char c;


c=getc(stream);

  



   

fscanf读文本文件里面的数字,既可以直接用%c,也可以用%d

用%c(char)读,可以读出3,1

用%d(int)读,也可以读出3,1

fseek(stream,3,SEEK_SET);

fscanf(stream,"%c",&c[0]);

fseek(stream,11,SEEK_SET);

fscanf(stream,"%c",&c[1]);

fseek(stream,ii,SEEK_SET);

fscanf(stream,"%d",&ln[i].frame);

fseek(stream,ii+8,SEEK_SET);

fscanf(stream,"%d",&ln[i].line);

只不过这两个3,1,一个是实际值3,1,一个是'3','1'




   

几个常见特殊字符的整型数字

 char

 int

 空格' '

 32

 TAB键

 9

 回车

 10

文件结束EOF

 -1

字符串结束符号

数字0(不是字符‘0’)


 



   

TAB字符处理要小心,经过到记事本copy/paste后,TAB键被转化成几个空格   

for(;str[i]==' '||str[i]=='        ';i++);

但经过到记事本copy/paste后,TAB键被转化成几个空格

所以系统总报warning:

tmp.c:58: warning: comparison is always false due to limited range of data type
 tmp.c:59:27: warning: character constant too long for its type



   


  

fgets字符串指针改成字符串数组,消灭了Segmentation fault错误

char *re,*rec;
 re=fgets(rec,100,srcstream);

出Segmentation fault错误

改成

char *re,rec[100];
 re=fgets(rec,100,srcstream);


错误消失



 

一个普通的显示文件内容 的函数

printfile(FILE *stream,char *filename,int or)
 {
 int i,re;
 char rec[100];
 FILE *tmpstream;
 if (or)
  {
  if((tmpstream=fopen(filename,"r"))==NULL)
     { printf("open file error/n");
       return 0;
      }
 for(i=0;(re=fgets(rec,100,tmpstream))!=NULL;i++) printf("[%d]:%s",i,rec);

显示文件内容时,每行加行号

if ((re=fclose(tmpstream))==-1) printf("close file error/n");

  } else
   {
 fseek(stream,0,SEEK_SET);
 for(i=0;(re=fgets(rec,100,stream))!=NULL;i++) printf("[%d]:%s",i,rec);
   }
 }

printfile(NULL,"testtmp.tmp",1 );

[0]:total 48
 [1]:1 macg macg 3301 Jan 16 02:16 file.c
 [2]:-rw-rw-r--  1 macg macg   52 Jan 16 02:19 Makefile
 [3]:1 macg macg 0 Jan 16 02:56 testtmp
 [4]:this is a testthis is a testthis is a testthis is a testthis is a testth
 [5]:-rw-rw-r--  1 macg macg 1428 Jan 16 02:56 tmp.o

if((stream=fopen("testtmp","r+"))==NULL)
 { printf("open file error/n");
   return 0;
 }
 printfile(stream ,NULL,0 );

 [0]:total 48
 [1]:-rw-rw-r--  1 macg macg 3301 Jan 16 02:16    file.c
 [2]:-rw-rw-r--  1 macg macg   52 Jan 16 02:19 Makefile
 [3]:    -rw-rw-r--  1 macg macg             0 Jan 16     02:56   testtmp
 [4]:this is a testthis is a testthis is a testthis is a testthis is a testth
 [5]:-rw-rw-r--  1 macg macg 1428 Jan 16 02:56 tmp.o





   

按SED/AWK原理,读记录/行的函数——是从文件中取第recno行

int getrecord(char *rec,FILE *stream,int recno)
 {
 int i,ret;
 char *re;
 for(i=0;i<=recno;i++) re=fgets(rec,100,stream); 数行,并一边数一边读行

 if(re==NULL) return 0;   如果所取行超过文件长度,则返回0
  else return 1;
 }

getrecord(record,stream,4);        取记录/行,取文件第四行
 printf("record is %s/n",record);
 printf("/n/n/n");
$ ./tmp
 record is       -rw-rw-r--  1 macg macg             0 Jan 16     02:56   testtmp


        


   

按SED/AWK原理,读字段的函数——从字符串中取第valueno字段

int getvalue(char *str,int valueno,char *value)
 {
 int i,j,vali;

数字段 
 for(i=0,vali=0;vali<valueno;vali++)
 {

 for(;str[i]==' '||str[i]==9;i++);

 for(;str[i]!=' '&&str[i]!=9&&str[i]!=0;i++);


 if(str[i]==0) return 0;
 }

取字段 
 for(;str[i]==' '||str[i]==9;i++);

 for(j=0;str[i]!=' '&&str[i]!=9&&str[i]!=0;i++,j++)
  {
   value[j]=str[i];
  }
 value[j]=0;

 }

for(i=0;i<20;i++)          
 {
 if(getvalue(record,i,val)) printf("no %d is %s/n",i,val);
 }
$ ./tmp
 record is       -rw-rw-r--  1 macg macg             0 Jan 16     02:56   testtmp    事先故意编辑过此行,加了几个TAB,连行尾也含TAB      

 no 0 is -rw-rw-r--
 no 1 is 1
 no 2 is macg
 no 3 is macg
 no 4 is 0
 no 5 is Jan
 no 6 is 16
 no 7 is 02:56
 no 8 is testtmp
 no 9 is




   

fputs(stren,streamwrite) 写入的串不带回车,fprintf(strea,"%s",str)写入的串也不带回车,所以,要写入文件回车,必须写入/n

fputs(stren,streamwrite);

 cat aaa.txt
008421aa


fprintf(streamwrite,"%s/n ",stren);

 cat aaa.txt
008421 
aa




    

文件很难进行“修改写”,建议文件追加

  

最好的办法就是写到新文件 ,或写到stdout再重定向入文件。然后把新文件覆盖旧文件



   

writefile(char *tmpfilename,char *srcfilename,struct Filerecord *frs,int frsi)-------修改文件的函数

实际是从 srcfile读出文件


把其中符合frs[j].recno的行,修改成frs[j].record


其他行不变,仍旧用srcfile读出的行


然后把这些行重新写入一个新文件tmpfile


writefile(char *tmpfilename,char *srcfilename,struct Filerecord *frs,int frsi
 o    tmpfilename 新文件名

 o    srcfilename 旧文件名

 o    struct Filerecord *frs  放置要修改的行的结构体数组
 struct Filerecord{
 char record[100];
 int recno;
 };
 struct Filerecord fr[20];


o    frsi  结构体数组的元素数量

writefile(char *tmpfilename,char *srcfilename,struct Filerecord *frs,int frsi)
 {
 int no,ret,i,j,bol;
 char *re,rec[100];
 FILE *tmpstream,*srcstream;


 if((tmpstream=fopen(tmpfilename,"w+ "))==NULL)  写入一个新
   {   printf("open file error/n");
      return 0;
     }
 if((srcstream=fopen(srcfilename,"r"))==NULL)
   { printf("open file error/n");
     return  0;
     }

 for(no=0;(re=fgets(rec,100,srcstream))!=NULL;no++)
 {
  for(j=0,bol=1;j<frsi&&bol;j++)
   {
    if(no==frs[j].recno) {
        bol=0;
        strcpy(rec,frs[j].record);
                                  }
    }


  fputs(rec,tmpstream);
 }

 if ((ret=fclose(tmpstream))==-1) printf("close file error/n");
 if ((ret=fclose(srcstream))==-1) printf("close file error/n");

 }

例子:去掉文件中行的TAB键和起首空格或TAB

执行:

    读出原文件每行,checkinvaild() ,看是否含有TAB,起首空格/TAB

    如果确实有,就对此行处理,重新组合,去掉TAB,起首空格/TAB,然后写入struct person fr[20]数组

writefile("testtmp.tmp","testtmp",fr,fri);
 ------------------the sick file----------------------------------------
 [0]:total 48
 [1]:-rw-rw-r--  1 macg macg 3301 Jan 16 02:16    file.c
 [2]:-rw-rw-r--  1 macg macg   52 Jan 16 02:19 Makefile
 [3]:    -rw-rw-r--  1 macg macg             0 Jan 16     02:56   testtmp
 [4]:this is a testthis is a testthis is a testthis is a testthis is a testth
 [5]:-rw-rw-r--  1 macg macg 1428 Jan 16 02:56 tmp.o
 --------------------modify line--------------------------------------
 fri is 2,the new line is following:
 [1]row:1 macg macg 3301 Jan 16 02:16 file.c
 [3]row:1 macg macg 0 Jan 16 02:56 testtmp
 -------------------new file---------------------------------------
 [0]:total 48
 [1]:1 macg macg 3301 Jan 16 02:16 file.c
 [2]:-rw-rw-r--  1 macg macg   52 Jan 16 02:19 Makefile
 [3]:1 macg macg 0 Jan 16 02:56 testtmp
 [4]:this is a testthis is a testthis is a testthis is a testthis is a testth
 [5]:-rw-rw-r--  1 macg macg 1428 Jan 16 02:56 tmp.o



   

fseek(stream,offset,mode)的三个mode

SEEK_SET:从开头数第几个offset


SEEK_CUR: 从当前数第几个offset


SEEK_END: 从结尾倒数第几个offset



  文件指针位置是从0(文件头)开始算的



fseek(stream,0,SEEK_SET); 
 


   
   int ftell(FILE *stream)      
 返回stream的当前指针位置

   

简单的取文件大小size的操作

fseek(stream,0,SEEK_END);先将指针指向文件尾部
 ret=ftell(stream); 再获得指针所指的当前地址
                  这个指向文件尾部的指针地址就是文件大小

 

取文件大小的操作对读写文件很有用,因为读写文件很不好判定文件结尾。







  

  文件例子

$ cat tmp.c
 #define DEBUG 0

 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>

 struct Filerecord{
 char record[100];
 int recno;
 };


 main()
 {
 char directory[100];

 #ifdef SYST
 getcwd(directory,100);
 printf("current directory is %s/n",directory);

 #endif

  filemanage();
 }

 filemanage()
 {
 int i,j,fri,ret;
 char record[100],val[50],modi_rec[100];
 FILE *stream;
 struct Filerecord fr[20];

 if((stream=fopen("testtmp","r+"))==NULL)
 { printf("open file error/n");
   return 0;
 }

 printf("------------------the sick file-----------------------/n");
 printfile(stream,NULL,0);

 for(i=0,fri=0;getrecord(record,stream,i);i++)
  {
 if (DEBUG) printf("no %d row is:%s/n",i,record);
 if(ret=findinvalid(record))
     {  
   for(j=0;getvalue(record,j,val);j++)
         {
 if (DEBUG)  printf("no %d's val is %s %d/n",j,val,val[0]);
       if(j==1) strcpy(modi_rec,val);
       if(j>1)  {
         strcat(modi_rec," ");
         strcat(modi_rec,val);
                 }
          }
  fr[fri].recno=i;
  strcpy(fr[fri].record,modi_rec);
 if (DEBUG) printf("fr[%d] is %d  %s/n",fri,fr[fri].recno,fr[fri].record);
  fri++;
      }
 if (DEBUG) printf("---------------------------------/n");

  }
 printf("--------------------modify line--------------------/n");
 printf("fri is %d,the new line is following: /n",fri);
 for(i=0;i<fri;i++)
  printf("[%d]row:%s",fr[i].recno,fr[i].record);

 if ((ret=fclose(stream))==-1)
   {
  printf("close file error/n");
  return 0;
   }
 writefile("testtmp.tmp","testtmp",fr,fri);
 printf("-------------------new file-----------------/n");
 printfile(NULL,"testtmp.tmp",1);
 }





 int findinvalid(char *str)
 {
 int i,ret;
 i=0;
 if(str[i]==' ')
 {
 if (DEBUG) printf("str[%d] == %d/n",i,str[i]);
  return 32;
 }
 for(i=0;str[i]!=9&&str[i]!=0;i++) ;
 if(str[i]==9)
 {
 if (DEBUG) printf("str[%d] == %d/n",i,str[i]);
  return 9;
 }  else
 {
 if (DEBUG) printf("str[%d] == %d/n",i,str[i]);
 return 0;
 }
 }
$ make
 gcc -g -c tmp.c
 tmp.c: In function 鈥榩rintfile鈥?
 tmp.c:158: warning: assignment makes integer from pointer without a cast
 tmp.c:158: warning: comparison between pointer and integer
 tmp.c:165: warning: assignment makes integer from pointer without a cast
 tmp.c:165: warning: comparison between pointer and integer
 gcc -o tmp tmp.o -g
$ ./tmp
 ------------------the sick file----------------------
 [0]:total 48
 [1]:-rw-rw-r--  1 macg macg 3301 Jan 16 02:16    file.c
 [2]:-rw-rw-r--  1 macg macg   52 Jan 16 02:19 Makefile
 [3]:    -rw-rw-r--  1 macg macg             0 Jan 16     02:56   testtmp
 [4]:this is a testthis is a testthis is a testthis is a testthis is a testth
 [5]:-rw-rw-r--  1 macg macg 1428 Jan 16 02:56 tmp.o
 --------------------modify line--------------------
 fri is 2,the new line is following:
 [1]row:1 macg macg 3301 Jan 16 02:16 file.c
 [3]row:1 macg macg 0 Jan 16 02:56 testtmp
 -------------------new file-------------------------
 [0]:total 48
 [1]:1 macg macg 3301 Jan 16 02:16 file.c
 [2]:-rw-rw-r--  1 macg macg   52 Jan 16 02:19 Makefile
 [3]:1 macg macg 0 Jan 16 02:56 testtmp
 [4]:this is a testthis is a testthis is a testthis is a testthis is a testth
 [5]:-rw-rw-r--  1 macg macg 1428 Jan 16 02:56 tmp.o