镜缘浮影 小人本住在 苏州的城外 家里有屋又有田 生活乐无边

标准I/O (三).文件的读取加工和保存

2016-12-30
wilmosfang
c
原文地址 http://soft.dog/2016/12/30/c-stdio-03/

前言

当前的计算系统除了包括对数据有 加工和处理 以外还有 搬运

这个 搬运 代表着 输入和输出 ,及 input/output ,简称 I/O

UNIX/Linux 的缔造者们将数据的 来源和目标 都抽象为 文件,所以在 UNIX/Linux 系统中 一切皆文件

一切皆文件 不仅仅对磁盘,还包括鼠标,键盘,显示器这些设备,那么对这些设备的操作也都抽象成了对 文件的I/O操作

关于 标准I/O 可以参看之前一篇文章 《标准I/O (一)》 ,关于C语言的API(linux)可以参看 Linux C API 参考手册 在线文档

这里分享一下我在学习 UNIX I/O 库过程中的笔记和心得


概要


标准IO库的常用函数

下面是一些 标准IO库中的常用函数

File *fopen(const char*path,const char *mode)
int fseek(FILE *stream, long offset, int whence)
long ftell(FILE *stream)
void rewind(FILE *stream)
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
int fgetc(FILE * stream)
int fclose(FILE *stream)
void *malloc(size_t size)
void free(void *ptr)

代码示例

给定一个任意文件a2(文件大小事先不确定),将文件中的内容按照数字、小写字母、大写字母、其他字符的顺序依次存入文件b2中,路径都在/home/file/下, 将a2、b2两个文件的内容合并后存到第三个文件c2中

#include <stdio.h> //标准io库
#include <malloc.h> //动态分配内存的库
#include <string.h> //字符串操作相关函数库


long getFileSize(char * fname) //计算一个文件的长度
{
  long length=0;
  FILE *fp=NULL;
  if(NULL == (fp=fopen(fname,"r")))
  {
    printf("cannot open file:%s\n",fname);
    return -1;
  }
  fseek(fp,0L,SEEK_END); //直接定位到文件结尾
  length = ftell(fp); //获取此时的指针位置,即文件长度(以字节计算),将偏移量保存
  return length; //返回偏移量,注意是long型
}


int main()
{
  FILE *fa=NULL,*fb=NULL,*fc=NULL; //定义三个文件的指针
  char *fileA="/home/emacs/file/a2";
  char *fileB="/home/emacs/file/b2";
  char *fileC="/home/emacs/file/c2"; //定义三个文件的路径
  char ch='\0',*ts=NULL,*tmpNum=NULL,*tmpLow=NULL,*tmpUp=NULL,*tmpOth=NULL,*tmpn=NULL,*tmpl=NULL,*tmpu=NULL,*tmpo=NULL; //定义操作指针
  long len=0; //长度的存放处

  if(NULL == (fb=fopen(fileB,"w+"))) //打开文件B
  {
    printf("cannot open file:%s\n",fileB);
    return -1;
  }
  if(NULL == (fc=fopen(fileC,"w+"))) //打开文件C
  {
    printf("cannot open file:%s\n",fileC);
    return -1;
  }
  if(NULL == (fa=fopen(fileA,"r"))) //打开文件A
  {
    printf("cannot open file:%s\n",fileA);
    return -1;
  }

  len=getFileSize(fileA); //获取文件A的长度
  if(1 > len) //检查没有内容就提示并返回
  {
    printf("%s is a empty file,please input something\n",fileA);
    return -1;
  }

  tmpNum=(char*)malloc(sizeof(char)*(len+1)); //分配内存来临时存放数字字符,之所以是len+1的长度,因为最极端的情况是全为一种类型的字符,并且给最后的'\0'预留一个位置,以便可以直接使用字符串函数进行处理
  tmpLow=(char*)malloc(sizeof(char)*(len+1)); //分配内存来临时存放小写字符
  tmpUp=(char*)malloc(sizeof(char)*(len+1)); //分配内存来临时存放大写字符
  tmpOth=(char*)malloc(sizeof(char)*(len+1)); //分配内存来临时存放其它字符
  ts=(char*)malloc(sizeof(char)*(len*2+1)); //分配内存来临时存放两份文件
 
  tmpn=tmpNum; 
  tmpl=tmpLow;
  tmpu=tmpUp;
  tmpo=tmpOth; //将控制指针进行就位

  while(EOF != (ch=fgetc(fa))) //遍历文件A中所有字符
  {
    if(ch >='a' && ch <= 'z') //是小写字符就存到tmpLow中
    {
      *tmpl=ch;
      tmpl++;
    }  
    else if(ch >='A' && ch <= 'Z') //是大写字符就存到tmpUp中
    {
      *tmpu=ch;
      tmpu++;
    }
    else if(ch >='0' && ch <= '9') //是数字字符就存到tmpNum中
    {
      *tmpn=ch;
      tmpn++;
    }
    else //是其它字符就存到tmpOth中
    {
      *tmpo=ch;
      tmpo++;
    } 
  }

  *tmpn='\0';
  *tmpl='\0';
  *tmpu='\0';
  *tmpo='\0';
  *ts='\0'; //结尾加'\0'形成字符串

  strcat(ts,tmpNum);
  strcat(ts,tmpLow);
  strcat(ts,tmpUp);
  strcat(ts,tmpOth); //根据数字、小写字母、大写字母、其他字符的顺序依次存入ts中

  if(1 != fwrite(ts,sizeof(char)*len,1,fb)) //将ts中的内容一次性写到文件B中
  {
    printf("file write error:%s\n",fileB);
    return -1;
  }
  
  tmpNum[0]='\0'; 
  strcpy(tmpNum,ts); //将ts中的内容(文件B中内容)临时保存到tmpNum中
  
  rewind(fa); //重置文件A指针
  if(1 != fread(ts,sizeof(char)*len,1,fa)) //将文件A中的内容读到ts中
  {
    printf("file read error:%s\n",fileA);
    return -1;
  } 
  ts[len]='\0';
  strcat(ts,tmpNum); //将tmpNum中的内容(文件B中内容)拼接到ts末尾

  if(1 != fwrite(ts,sizeof(char)*len*2,1,fc)) //将ts中的内容(文件A加文件B中的内容)写到文件C中
  {
    printf("file write error:%s\n",fileC);
    return -1;
  }

  free(tmpNum);
  free(tmpLow);
  free(tmpUp);
  free(tmpOth);
  free(ts); //释放所有动态内存
  fclose(fa);
  fclose(fb);
  fclose(fc); //关闭所有文件指针,回收系统资源
  return 0;
}

Note: 文件打开数是一种系统资源,是有上限的,虽然程序退出后,系统会帮忙清理,但在程序设计中,打开文件,使用完后进行手动关闭是一种很好的习惯,这样可以有效避免缓存未刷新的潜在隐患,也可以更加节约资源

编译执行

emacs@ubuntu:~/file$ > b2 ; > c2
emacs@ubuntu:~/file$ cat b2;cat c2 
emacs@ubuntu:~/file$ cat a2
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
emacs@ubuntu:~/file$ 
----------
emacs@ubuntu:~/c$ alias gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc convAtoBsumtoC.x convAtoBsumtoC.c 
emacs@ubuntu:~/c$ ./convAtoBsumtoC.x 
emacs@ubuntu:~/c$
----------
emacs@ubuntu:~/file$ cat a2
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
emacs@ubuntu:~/file$ cat b2
123123123123123123123abcabcabcabcabcabcabcABCABCABCABCABCABCABC!@#
!@#
!@#
!@#
!@#
!@#
!@#
emacs@ubuntu:~/file$ cat c2
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
aA1!bB2@cC3#
123123123123123123123abcabcabcabcabcabcabcABCABCABCABCABCABCABC!@#
!@#
!@#
!@#
!@#
!@#
!@#
emacs@ubuntu:~/file$

编译执行过程中没有报错,从结果来看,b2,c2文件中的内容变化也符合预期


总结

以下这些函数可以应对绝大部分的IO需求

  • fopen/fclose
  • fread/fwrite
  • fseek/ftell/rewind
  • fgetc

通过各方面资料弄懂其参数的意义和返回值的类型,是熟练掌握的基础

原文地址 http://soft.dog/2016/12/30/c-stdio-03/

评论