前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >文件I/O (一).非缓冲IO实现mycopy

文件I/O (一).非缓冲IO实现mycopy

作者头像
franket
发布2021-09-16 09:55:20
4770
发布2021-09-16 09:55:20
举报
文章被收录于专栏:技术杂记技术杂记

前言

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

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

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

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

关于 标准I/O 可以参看之前的文章 《标准I/O (一)》 ,类Unix系统中除了 标准I/O 还有 文件I/O,可以完成相同工作,关于C语言的API(linux)可以参看 Linux C API 参考手册 在线文档

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


概要


文件I/O

文件I/O 可以实现 标准I/O 一样的功能,包括打开文件,读取文件,写入文件,关闭文件等操作

文件I/O 主要包含:open/read/write/lseek/close 几个函数,大多数操作都可以由这几个函数来完成,相对于 标准I/O 封装得更为简单

文件I/O 也被称为不带缓冲的I/O(unbuffered I/O),每一个read和write都调用内核中的一个系统调用

Note: 之所以是不带缓冲的,也是相对于标准I/O而言,标准I/O库使用了缓冲技术,而这正是产生很多问题,引起许多混淆的部分,文件I/O进行了有效的规避,缓冲区由开发者自己来定义和管理

Tip: 文件I/O 并不是ISO C的组成部分,而 标准I/O 属于ISO C的组成部分


文件IO库的常用函数

下面是一些 文件IO库中的常用函数

代码语言:javascript
复制
int open( const char *pathname, int flags)
int open( const char *pathname, int flags, mode_t mode)
ssize_t read(int fd, void *buf, size_t count)
ssize_t write(int fd, const void *buf, size_t count)
off_t lseek(int fildes, off_t offset, int whence)
int close(int fd)

IO库的比较

I/O库

文件I/O

标准I/O

缓冲方式

非缓冲I/O

缓冲I/O

操作对象

文件描述符

流(FILE *)

打开

open()

fopen()/freopen()/fdopen()

read()

fread()/fgetc()/fgets()

write()

fwrite()/fputc()/fputs()

定位

lseek()

fseek()/ftell()/rewind()/fsetpos()/fgetpos()

关闭

close()

fclose()


代码示例

使用主函数传参的方式,实现图片的拷贝

main(int argc,char *argv[])

代码语言:javascript
复制
#./mycopy  a.jpg  b.jpg
# diff a.jpg b.jpg

代码示例

代码语言:javascript
复制
#include <stdio.h>  //标准IO函数
#include <unistd.h> //文件IO函数包含其中,缺少这个头文件read,write,close 会报错
#include <fcntl.h> //open函数包含其中,还有一些重要的宏定义


int main(int argc,char *argv[]) //带参数的主函数
{
  int fr=0,fw=0,rres=0,res=-1; 
  char tmpc='\0';
  char *fileA="/home/emacs/file/a.png";
  char *fileB="/home/emacs/file/b.png"; //定义与初始化各种变量
  
  if(3 != argc)  //进行参数检查,不符合则提示并返回
  {
    printf("argument number error: need only two args <fileA> and <fileB>\n");
    return res;
  }
  fileA=argv[1]; //将第一个参数作为A文件
  fileB=argv[2]; //将第二个参数作为B文件
  
  if(-1 == (fr=open(fileA,O_RDONLY))) //以只读的方式打开A文件
  {
    printf("cannot open file:%s\n",fileA);
    return res;
  }
  if(-1 == (fw=open(fileB,O_RDWR|O_CREAT|O_TRUNC,0600))) //以读写的方式打开B文件
  {
    printf("cannot open file:%s\n",fileB);
    return res;
  }
  while( 1 == (rres=read(fr,&tmpc,sizeof(char)))) //循环读出A文件中的内容,一次读取一个字符的长度(这个长度可以适当加长以减少读取次数来提升读取效率)
  {
    if (1 != write(fw,&tmpc,sizeof(char))) //将读出的内容依次写到文件B中
    {
      printf("write error on:%s\n",fileB);
      break;
    }
  }
  if(0 == rres) //如果返回结果是0,就代表读取完毕正常结束
  {
    printf("copy done:from %s to %s\n",fileA,fileB);
    res=0;
  }
  if(-1 == rres) //如果返回结果是-1,就代表读取出错
  {
    printf("read error on %s\n",fileA);
  }
  close(fr);
  close(fw); //回收文件描述符,刷新到硬盘
  return res;
}

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

编译执行

代码语言:javascript
复制
emacs@ubuntu:~/c$ alias gtc 
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc mycopy.x mycopy.c
emacs@ubuntu:~/c$ du -sh /home/emacs/file/a.png 
88K	/home/emacs/file/a.png
emacs@ubuntu:~/c$ du -sh /home/emacs/file/b.png 
du: 无法访问"/home/emacs/file/b.png": 没有那个文件或目录
emacs@ubuntu:~/c$ ./mycopy.x /home/emacs/file/a.png  /home/emacs/file/b.png 
copy done:from /home/emacs/file/a.png to /home/emacs/file/b.png
emacs@ubuntu:~/c$ diff /home/emacs/file/a.png  /home/emacs/file/b.png
emacs@ubuntu:~/c$

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


总结

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

  • open
  • close
  • read
  • write
  • lseek

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

原文地址

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 概要
    • 文件I/O
      • 文件IO库的常用函数
        • IO库的比较
          • 代码示例
            • 编译执行
        • 总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档