本文讲述Linux C 中对文件的操作。分为以下几部分内容:
打开方式 (包含 创建  能力) 读、写操作 
读、写单个字符 读、写字符串 格式化读、写操作 
 
数据块的读写 错误码判断与操作 位置指针移动 关闭文件 删除文件  
以上操作涉及的函数#include <stdio.h>即可。
#打开方式 
FILE *fopen(const char *path, const char *mode);
 
打开一个文件可以用以上三个函数来实现,该函数执行后则会有相应的流与FILE*绑定。errno中查询。
#####FILE *fopen(const char *path, const char *mode);   #####
fopen()的第一个参数path用于指定要打开的文件名,第二个参数mode指定打开的方式模式。打开文件的模式可以是下列表中的某一项,或者某几项的组合。下表中给出了子模式的含义,且与其它模式的区别。
|模式|文件不存在|读写能力|读位置|写位置|写操作对原文件内容影响|
r: read w:write a:append b:binary   
 
有些时候会看到文件打开模式除了上面的几种情况或由这几种情况的任意组合外,还会有和b的组合,含义是:b表示以 二进制文件方式  操作文件,否则以 文本文件方式  操作。
模式w+和a+虽然都具有读权限,但实践中有可能出现无法读出文件中的内容,原因如下:
使用fopen时w+一开始会把文件清空,这时候去读是没有内容的;写操作完毕后文件已经有内容了,这时候直接去读取,由于文件指针位置在文件末尾,所以仍然无法读取到内容,得把文件指针位置移动到文件之前的位置,才有内容可以读出。 
使用a+时,情形同w+相似,文件指针位置是在文件的结束位置,导致读不出内容。 
 
示例代码将在文章的下半部分文件位置指针移动 中给出。
#####FILE *fdopen(int fd, const char *mode);#####    
fdopen()是使用文件描述符fd来操作文件流的。fopen()相似。差别在于   
fdopen()在使用模式w和w+时,并不会对原有的文件内容进行清空操作;   fdopen()可以直接使用STDIN_FILENO(mode要具备r权限)、STDOUT_FILENO(mode要具备w权限)、STDERR_FILENO(mode要具备w权限)来操作标准输入输出流。   
相关:函数fileno()可以获取某个文件的fd。
int fileno(FILE *stream);
 
示例,以下代码演示从终端命令行中敲入一个字符并打印出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #incluce <unistd.h>  #incluce <stdio.h>  #incluce <stdlib.h>  int  main ()      FILE* file = fdopen(STDIN_FILENO, "r" );     if  (file == NULL ) {         printf ("fopen fail:%s " , filePath);         perror("" );         exit (EXIT_FAILURE);     }     int  ch = fgetc(file);     if  (ch == EOF) {         if  (errno != 0 ) {             perror("fgetc fail:" );             printf ("feof %d \n" , feof(file));         } else  {             printf ("read file end. %d \n" , feof(file));         }     } else  {         printf ("fgetc:%c \n" , ch);     }          fclose(file);     return  EXIT_SUCCESS; } 
#####FILE *freopen(const char *path, const char *mode, FILE *stream); #####freopen()的功能是将一个已经打开的流(参数中的stream,可以是文件也可以是标准输出输入流)重定向到另一个流(函数返回值)中去,重定向的位置由参数path决定。
示例代码:将标准输出流重定向到指定文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #incluce <unistd.h>  #incluce <stdio.h>  #incluce <stdlib.h>  int  main ()      FILE * tmp;          if ((tmp = freopen("/Users/sodino/reopen.txt" , "w" , stdout )) == NULL ) {                  fprintf (stderr ,"error1:redirecting stdout \n" );     } else  {                  printf ("This will go into a file. %d\n" , (tmp == stdout ));     }          if ((tmp = freopen("/Users/sodino/reopen.2.txt" , "w" , stdout )) == NULL ) {         fprintf (stderr ,"error1 redirecting stdout\n" );     } else  {         printf ("2This will go into a file. %d\n" , (tmp == stdout ));     }          fclose(tmp);     return  EXIT_SUCCESS; } 
#读、写操作 读、写单个字符##### 
int fgetc(FILE *stream);
 
fgetc()从指定的流中读取下一个字符,把unsigned char强制转换成int。getc()功能与fgetc()一致,只是在函数执行时可能会对流多做一次评估验证。getchar()功能等同于getc(stdin)。
以上三个函数在正常执行时都会返回读取到的字符int,在发生错误或已经读到流的结束位置则返回EOF。
写:
int fputc(int c, FILE *stream);
 
fputc()将int转化为unsigned char并写入流中。putc()功能与fputc()一致,只是在函数执行时可能会对流多做一次评估验证。putchar()功能等同于putc(c, stdout)。
三个函数在正常执行时会将写入的字符c返回,在执行发生错误时则返回EOF。
Copy Demo 
下面的示例代码演示复制一个文件的过程,copyFile_str()是逐字节复制,而copyFile_data()是每次8KB的数据块在复制,所以效率会高一些。fread()及fwrite()将会在下文中继续提到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 #include  <stdio.h>  #include  <stdlib.h>  static  long  copyFile (FILE*src, FILE*dest)      if  (src == NULL  || dest == NULL ) {         return  -1 ;     }          if  (ftell(src) > 0 ) {         rewind(src);     }          int  res = 0 ;     long  count = 0 ;      while  (1 ) {         int  ch = fgetc(src);         if  (ch == EOF) {             if  (!feof(src)) {                 printf ("fgetc() break res=-1. count=%ld \n" , count);                 res = -1 ;              } else  {                              }             break ;         } else  {             int  tmp = fputc(ch, dest);             if  (tmp == EOF) {                 res = -1 ;                 printf ("fputc() break res=-1. count=%ld \n" , count);                 break ;             }             count ++;             continue ;          }     }          return  (res == -1 )?-1L :count; } static  long  copyFile_str (const  char  *srcPath, const  char  *destPath)      FILE * src = fopen(srcPath,"r" );     FILE * dest = fopen(destPath,"w" );          long  res = copyFile(src, dest);     fclose(dest);     fclose(src);          return  res; } static  long  copyFileData (FILE*src, FILE*dest)      if  (src == NULL  || dest == NULL ) {         return  -1 ;     }          fseek(src, 0 , SEEK_END);          long  file_size = ftell(src);          rewind(src);          int  res = 0 ;     long  count = 0 ;           const  int  LEN_DATA = 8 *1024 ;     unsigned  char  data[LEN_DATA];     while  (1 ) {                  long  current = ftell(src);                  long  leave = file_size - current;         int  readSize = LEN_DATA;         if  (leave == 0 ) {                          printf ("leave == 0 feof.\n" );             break ;         } else  if  (leave < LEN_DATA) {                          readSize = (int )leave;         }                  size_t  res_t  = fread(&data, 1 , readSize, src);         if  (res_t  == readSize) {                          size_t  w_res = fwrite(&data, 1 , res_t , dest);             count += w_res;             if  (res_t  == w_res) {                 printf ("write=%ld res_t=%zu \n" , count, res_t );             } else  {                 printf ("fwrite fail. \n" );                 res = -1 ;                 break ;             }         } else  if  (feof(src) != 0 ){             printf ("feof res_t=%zu \n" , res_t );             break ;         } else  {             printf ("fread fail...\n" );             res = -1 ;             break ;         }     }               return  (res == -1 )?-1 :count; } static  long  copyFile_data (const  char  * srcPath, const  char  *destPath)      FILE * src = fopen(srcPath,"r" );     FILE * dest = fopen(destPath,"w" );          long  res = copyFileData(src, dest);          fclose(dest);     fclose(src);          return  res; } int  main (int  argc, char  **argv)      char  *srcPath = "/Users/sodino/workspace/xcode/CString/CString/a.out" ;     char  *destPath = "/Users/sodino/workspace/xcode/CString/CString/a1.out" ;     long  res = copyFile_str(srcPath, destPath);          if  (res == -1 ){         printf ("copyFile_str() fail. \n\n\n" );     } else  {         printf ("copyFile_str() count=%d. \n\n\n" , res);     }          printf ("do copyFile_data() \n" );     res = copyFile_data(srcPath, destPath);     printf  ("copy.file.length=%ld \n" , res);     if  (res == -1 ) {         return  EXIT_FAILURE;     } else  {         return  EXIT_SUCCESS;     } } 
#####读、写字符串##### 
char *fgets(char *s, int size, FILE *stream);
 
从流中读取一个小于size长度的字符串,并将读到的字符串s返回。在读取过程中如果碰到EOF或换行,则把已经读到的字符串返回不再继续往下读取。返回的字符串会自动加上字符串终止符/0,字符串终止符是算在size里面的,所以得到的字符串一直是小于size的。
写:
int fputs(const char *s, FILE *stream);
 
fputs()会将参数中字符串s去掉字符串终止符/0然后写入指定的流中去。puts()相当于fputs(s, stdout),即把字符串输出到标准输出流中去,且会在字符串后面自动加上换行符。
当函数正常执行时,则返回一个非0的数字代表写入的字符个数;否则返回EOF。
#####格式化读、写操作######include <stdio.h> 
int fscanf(FILE *stream, const char *format, …);  // 格式化读
 
之前的文章已经讲过了printf() 强大的日志打印功能,在文件流的读、写上fprintf()相当于是把printf()的内容写入到指定的流中去;fscanf()则是fprintf()的反操作,读。
示例代码将在文件位置指针移动 中给出。
#数据块的读写 
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
 
fread()用于流操作中数据块的读取。将读到的数据存储于第一个参数ptr中,第二个参数size告诉函数每个数据块的大小,第三个参数nmemb指明要读取数据块的数量,第四个参数stream指明读取的数据流。nmemb;当执行出现错误或遇到了文件结束的位置时,则返回0或比nmemb小的数。至于是出现错误还是文件结束导致的非正常执行,则要通过feof()和ferror()来判断。
fwrite()用于流操作中的数据块写入。第一个参数ptr表示要写入的数据,第二个参数size告诉函数每个数据块的大小,第三个参数nmemb指明要写入数据块的数量,第四个参数stream指明写入的数据流。nmemb;当执行出现错误,则返回0或比nmemb小的数。
这两个方法经常用于二进制文件的操作,所以打开方式上要与b模式组合。
示例代码:批量读写 sturct student:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include  <stdio.h>  #include  <stdlib.h>  struct  student  {    char  name[25 ];     char  sex;     struct  {         int  year;         int  month;         int  day;     }birthday;     float  BWH[3 ]; }; int  main (int  argc, char  ** argv)      struct  student  pes [3] = {"jack ma" ,'M' ,1985 ,2 ,22 ,80 ,95 ,60.5 },         {"pony" ,'F' ,1984 ,1 ,2 ,57 ,60 ,78 },         {"robin" ,'F' ,1995 ,3 ,2 ,77.0 ,90 ,77.5 }     };          int  i;     FILE *fpr;     struct  student  b ;     fpr = fopen("W.DAT" ,"wb+" );     for (i=0 ;i<3 ;i++) {         fwrite(pes+i,sizeof (struct student),1 ,fpr);     }                         fseek(fpr,0L ,SEEK_SET);          for (i=0 ;i<3 ;i++) {         fread(&b,sizeof (struct student),1 ,fpr);         printf ("%7s  " ,b.name);          printf ("%c   " ,b.sex);         printf ("%04dY %02dM %02dD  " ,b.birthday.year,b.birthday.month,b.birthday.day);         printf ("%5.1f %5.1f %5.1f" ,b.BWH[0 ],b.BWH[1 ],b.BWH[2 ]);         printf ("\n" );     }          fclose(fpr);          return  EXIT_SUCCESS; } 
#错误码判断与操作 
int feof(FILE *stream);
 
feof()判断文件是否处于文件结束位置,如文件结束,则返回值为1,否则为0。ferror(文件指针)检查文件在用各种输入输出函数进行读写时是否出错。 如ferror返回值为0表示未出错,否则表示有错。 
clearerr()用于清除出错标志和文件结束标志,使它们为初始值0。
#文件位置指针移动 
long ftell(FILE *stream);
 
ftell()用于获知当前文件指针的位置。fseek()用于改变文件指针的位置,第二个参数offset是指参照whence后指针进一步的偏移量。第三个参数whence有三种赋值及其含义分别为:SEEK_SET : 文件指针回到文件初始位置。SEEK_CUR : 文件指针不变,仍处于当前位置。一般配合offset移动文件指针到当前位置前后若干字节。SEEK_END : 文件指针跳到文件结束的位置。errno中。rewind()相当于fseek(file, 0, SEEK_SET)。即直接跳转到文件开头位置。
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include  <stdio.h>  #include  <stdlib.h>  int  main (int  argc, char  **argv)      char  *filePath = "/Users/sodino/workspace/xcode/CString/CString/test.txt" ;     FILE* file = fopen(filePath, "w+" );     printf ("integer descriptor:%d \n" , fileno(file));     if  (file == NULL ) {         printf ("fopen fail:%s " , filePath);         perror("" );         exit (EXIT_FAILURE);     }          fprintf (file, "%s %d" , "My_Age_is" , 19 );      fseek(file, 0 , SEEK_SET);      char  arrRead[100 ];     int  age;     fscanf (file, "%s %d" , arrRead, &age);          printf ("read: %s %d \n" , arrRead, age);     fclose(file);          return  EXIT_SUCCESS; } 
#关闭文件 
int fclose(FILE *stream);stream进行操作的行为结果则都是未知的。所以请及时对stream置NULL。
 
#删除文件 
int remove(const char *pathname);   Thread-Safe remove()函数可以从文件系统中删除指定路径下的文件(夹),内部实现中使用unlink()删除文件,使用rmdir()删除文件夹。
 
如果指定的路径是其所代表的文件(夹)的最后一个链接(参照硬链接和软链接 )且没有被其它进程后引用,则该文件(夹)会被立即删除,其所占用的空间也可以被重新利用。但如果有其它进程仍在引用着该文件(夹),则删除和空间的回收操作要等到其它进程释放了当前文件(夹)的引用之后才会继续进行。
如果指定的路径是一个软链接文件,则只会删除该软链接文件,而非其指向的原始文件。