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

标准I/O (二).缓冲型IO库函数

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

前言

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

这个 搬运 代表着 输入和输出 ,及 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 fclose(FILE *stream)
void *malloc(size_t size)
void free(void *ptr)

代码示例

给定一个已知文件a(大小确定,文件内容自行用vi输入n个字符),将文件中的内容倒序后存入文件b中。路径都在/home/emacs/file/下

#include <stdio.h> //标准IO库
#include <malloc.h> //动态内存分配函数库

int slen(char *s) //求字符串长度 (string.h中的strlen函数有相同功能)
{
  int i=0;
  for(;*s!='\0';s++)i++; //使用i进行计数
  return i;
}

char * srev(char *s) //字符串翻转(因为没有现成的翻转函数,所以得自己写)
{
  int i=0,len=slen(s); 
  char tmpc='\0'; //之所以将各种变量定义后,同时进行赋值初始化,是为了避免不可知不可控的状况
  for(i=0;i<len/2;i++) //从字符串两边往中间依次进行收尾对调
  {
    tmpc=s[i];
    s[i]=s[len-1-i];
    s[len-1-i]=tmpc;
  }
  return s;
}

int main()
{
  FILE *fp=NULL,*fb=NULL;
  char *fileA="/home/emacs/file/a";
  char *fileB="/home/emacs/file/b";
  char *tmps=NULL;
  long len=0; //进行各种赋值初始化

  if(NULL == (fb=fopen(fileB,"w+"))) //以写也可读的方式打开文件B,出错则提醒并退出
  {
    printf("cannot open file:%s\n",fileB);
    return -1;
  }
  if(NULL == (fp=fopen(fileA,"r+"))) //以读也可写的方式打开文件A,出错则提醒并退出
  {
    printf("cannot open file:%s\n",fileA);
    return -1;
  }

  fseek(fp,0L,2); //定位到文件末尾
  len=ftell(fp); //取出当前的位置偏移量,是为了获取文件长度
  if(1 > len) //如果文件为空,就提醒并退出					    
  {
    printf("%s is a empty file,please input something\n",fileA);
    return -1;
  }
  rewind(fp); //重新归置文件指针位置到文件开头
  if (NULL == (tmps=(char*)malloc(sizeof(char)*(len+1)))) //申请一段长度为len+1个char的空间,出错则提醒并退出
  {
    printf("no enough memory\n");
    return -1;	
  } 
  if(1 != fread(tmps,sizeof(char)*len,1,fp)) //从文件A中读取len个char长度的内容写到tmps的临时空间中,出错则提醒并退出
  {
    printf("file read error:%s\n",fileA);
    return -1;
  }  
  tmps[len]='\0'; //将结尾加上字符串结束符,之所以进行这一步,是为了给翻转作准备的,没有'\0'结尾,很可能产生乱码
  srev(tmps); //将字符串内容进行翻转
  if(1 != fwrite(tmps,sizeof(char)*len,1,fb))  //从tmps的临时空间中读取len个char长度的内容写到文件B中,出错则提醒并退出
  {
    printf("file write error:%s\n",fileB);
    return -1;
  }
  free(tmps); //释放tmps的临时空间
  fclose(fp); //关闭文件A
  fclose(fb); //关闭文件B,这步操作可以有效避免缓存未刷新到硬盘的潜在隐患
  return 0;
}

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

编译执行

emacs@ubuntu:~/c$ alias gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc acopytob.x acopytob.c 
emacs@ubuntu:~/c$ cat /home/emacs/file/a
1234567890abcdef
emacs@ubuntu:~/c$ cat /home/emacs/file/b
emacs@ubuntu:~/c$ ./acopytob.x 
emacs@ubuntu:~/c$ cat /home/emacs/file/a
1234567890abcdef
emacs@ubuntu:~/c$ cat /home/emacs/file/b

fedcba0987654321emacs@ubuntu:~/c$ 
emacs@ubuntu:~/c$

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


总结

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

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

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

原文地址 http://soft.dog/2016/12/26/c-stdio-02/

评论