当前的计算系统除了包括对数据有 加工和处理 以外还有 搬运
这个 搬运 代表着 输入和输出 ,及 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 主要包含: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库中的常用函数
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)
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[])
#./mycopy a.jpg b.jpg
# diff a.jpg b.jpg
代码示例
#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: 文件打开数是一种系统资源,是有上限的,虽然程序退出后,系统会帮忙清理,但在程序设计中,打开文件,使用完后进行手动关闭是一种很好的习惯,这样可以有效避免缓存未刷新的潜在隐患,也可以更加节约资源
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需求
通过各方面资料弄懂其参数的意义和返回值的类型,是熟练掌握的基础
原文地址
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。