【Linux学习笔记】7. Linux文件IO详解(附代码实例)

Linux文件I/O

# 前置知识

  • Linux文件I/O分为系统IO和标准IO,常用于系统编程

  • 系统I/O通过文件描述符 fd 来操作文件

  • 标准I/O通过文件流 FILE* 来操作文件

  • Linux下可以使用man命令来查看使用手册

学习和使用这些API最快的途径是利用系统自带的man查看手册,查看系统IO可以用man 2 open, 查看标准I/O可以用man 3 fopen

关于linux中man 1 2 3 … 的区别 :

1、Standard commands (标准命令)
2、System calls (系统调用)
3、Library functions (库函数)
4、Special devices (设备说明)
5、File formats (文件格式)
6、Games and toys (游戏和娱乐)
7、Miscellaneous (杂项)
8、Administrative Commands (管理员命令)
9、 其他(Linux特定的), 用来存放内核例行程序的文档。

# 系统I/O

常用的系统I/O接口有:

  • open()
  • read()
  • write()
  • close()
  • lseek()
1. open()

使用命令man 2 open查看使用手册

头文件:

使用open()函数需要包含以下头文件:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

函数原型:

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

函数参数:

参数意义
pathname打开/创建的文件名
flags文件的打开模式,常用O_RDONLY, O_WRONLY, O_RDWR
mode创建文件时需要指定,用来设置创建文件的权限(rwx)

返回值:

  • 返回值类型:int
  • 调用成功时返回一个文件描述符fd
  • 调用失败时返回-1,并设置errno

关于函数的参数说明:

第二个参数flags可以指定为以下宏:

  • O_RDONLY : 只读打开
  • O_WRONLY : 只写打开
  • O_RDWR : 读写打开
  • O_APPEND:以追加模式打开
  • O_CREAT:若文件不存在,则创建该文件,同时需要指定第三个参数
  • O_SYNC:使每次write都等到物理I/O操作完成
  • O_TRUNC:若此文件存在,并以读写或只写打开,则文件长度为0(打开文件的同时将文件中的内容清除)
  • O_EXCL:若同时设置O_CREAT标志且文件已存在,则会出错。可用于测试文件是否存在
  • O_NOCTTY:若打开的文件是终端设备,则不将此设备设置为进程的控制终端
  • O_NONBLOCK:若打开的文件是管道、块设备文件或字符设备文件,则后续的I/O操作均设置为非阻塞方式

以上关于flags的参数只有前三个指定读写方式的参数必须唯一,其他都可以用或运算符|同时指定多个宏。

第三个参数mode可以指定为以下宏:

image-20220420134637818

linux环境下使用命令man 2 open打开的手册中关于flags指定了O_CREAT时的第三参数选项,学过Linux文件权限都能理解手册的内容。

2. read()

使用命令man 2 read查看使用手册

头文件:

使用read()函数需要包含头文件:#include <unistd.h>

函数原型:

ssize_t read(int fd, void *buf, size_t count);

函数参数:

参数意义
fd即将读取文件的文件描述符
buf存储读入数据的缓冲区
count将要读入的数据的个数

返回值:

  • 返回值类型是:ssize_t,32位机上等同于int
  • 成功时返回读取的字节数
  • 出错时返回EOF,读到文件末返回0
3. write()

使用命令man 2 write查看使用手册

头文件:

使用write()函数需要包含头文件:#include <unistd.h>

函数原型:

 ssize_t write(int fd, const void *buf, size_t count);

函数参数:

参数意义
fd即将读取文件的文件描述符
buf要写入的数据缓冲区
count写入数据的个数,大小不应该大于buf大小

返回值:

  • 返回值类型为:ssize_t
  • 成功时返回写入的字节数
  • 出错时返回EOF,读到文件末返回0
4. close()

使用命令man 2 close查看使用手册

头文件:

使用close()时需要包含头文件: #include <unistd.h>

函数原型:

int close(int fd);

函数参数:

  • fd: 要关闭的文件描述符。

返回值:

  • 关闭成功时返回0,出错时返回EOF.。

说明:

  • 程序在结束时会自动关闭所有打开的文件。
  • 文件被关闭后,再对文件进行任何操作都是无意义的。
5. lseek()

使用命令man 2 lseek查看使用手册

头文件:

使用lseek()函数需要包含头文件:

#include <sys/types.h>
#include <unistd.h>

函数原型:

 off_t lseek(int fd, off_t offset, int whence);

函数参数:

参数意义
fd文件描述符
offset偏移量,可正可负
whence指定一个基准点,基准点+偏移量等于当前位置

返回值:

  • 返回值类型为:off_t, 32位机等同于int
  • 成功时则返回目前的读写位置, 也就是距离文件开头多少个字节
  • 出错时返回-1, errno 会存放错误代码.

关于函数的参数说明:

第三个参数whence

  • SEEK_SET:基准点为文件开头
  • SEEK_CUR:基准点为当前位置
  • SEEK_END:基准点为文件长度(可以理解为文件末尾)

image-20220420172549752

系统IO实例——文件复制
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	if(argc != 3) {	//argv第一个参数为自身文件名,因此需要再指定两个文件
		printf("error\n");
		exit(1);	//使用exit()需要包含头文件<stdlib.h>
	}
	//打开文件
	int fd_from = open(argv[1], O_RDONLY);	//第二个参数为copy的源文件
	if(-1 == fd_from) {
		perror("open1");
		exit(2);
	}
	int fd_to = open(argv[2], O_WRONLY | O_CREAT, S_IRWXU);	//第三个参数为copy的目标文件
	if(-1 == fd_to) {
		perror("open2");
		exit(3);
	}
	
	char buff[128] = {0};
	ssize_t ret;
	while(1) {	//每次读写128个字节,直到文件结束
		ret = read(fd_from, buff, sizeof(buff) - 1);
		if(-1 == ret) {
			perror("read");
		}
		else if(0 == ret) {	//read()返回0则文件结束了
			printf("finish copy\n");
			break;	
		}
		ret = write(fd_to, buff, ret);
		if(-1 == ret) {
			perror("write");
		}
	}
	//关闭文件
	close(fd_from);
	close(fd_to);
	return 0;
}

# 标准I/O

  • 与系统I/O不同的是,标准I/O带有缓冲区,可以减少系统调用,提高系统效率。

常用的标准I/O接口有:

  • fopen()
  • fread()
  • fwrite()
  • fclose()

使用标准I/O只需包含头文件:#inlcude <stdio.h>

1. fopen()

函数原型:

FILE *fopen(const char *path, const char *mode);

函数参数:

参数意义
path打开/创建的文件名
mode文件的打开模式

关于mode的选项有:r只读,r+读写;w只写,w+读写,a追加写,a+追加读写。其中除了r和r+其他都会在文件不存在时创建文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ypi7jjAB-1650468046692)(https://cdn.jsdelivr.net/gh/Chen-Mxn/mx-picgo-image/20220420173628.png)]

返回值:

  • 成功时返回一个文件流指针:FILE*
  • 失败时返回NULL
2. fread()

函数原型:

 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

函数参数:

参数意义
ptr接收数据的地址,最小尺寸size*nmemb字节
size每次读取的一个单元的大小,单位字节
nmemb读取的单元个数
stream即将读取文件的文件流

返回值:

  • 返回值类型为:size_t,在32位机等价于unsigned int
  • 成功时返回实际读取的单元个数
  • 文件结束或读取出错时返回0或一个小于nmemb的数
3. fwrite()

函数原型:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

函数参数:

参数意义
ptr要写入文件的数据的地址
size每次写入的一个单元的大小
nmemb写入的单元个数
stream即将写入文件的文件流

返回值:

  • 返回值类型为:size_t
  • 成功时返回实际写入的单元个数
  • 出错时返回一个与nmemb不同的数
4. fclose()

函数原型:

int fclose(FILE *stream);

函数参数:

  • stream: 要关闭的文件流指针

返回值:

  • 成功时返回0
  • 失败时返回EOF,并设置errno
标准IO实例——文件复制
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	if( argc != 3) {
		printf("error! please input 2 file name");
		exit(1);
	}
	//打开文件
	FILE *fp_from = fopen(argv[1], "r");	//只读
	if(NULL == fp_from) {
		perror("fopen1");
		exit(2);
	}
	
	FILE *fp_to = fopen(argv[2], "w");	//只写
	if(NULL == fp_to) {
		perror("fopen2");
		exit(3);
	}

	size_t size;
	char buff[128] = {0};
	while(1) {	//每次128字节不断读写
		size = fread(buff, 1, sizeof(buff) - 1, fp_from);
		if(0 == size) {	//fread()返回0 文件结束即复制完成
			printf("finish copy\n");
			break;
		}

		size = fwrite(buff, 1, size, fp_to);
		if(0 == size) {
			perror("fwrite");
		}
        //每次读写完要把buff清空防止出错
		memset(buff, 0, sizeof(buff));
	}
	//关闭文件
	fclose(fp_from);
	fclose(fp_to);
	return 0;
}

# 系统IO与标准IO的区别

最大的区别在于:

  • 标准IO引用了缓冲机制,在每次执行标准IO操作时,所有的操作都在缓存区中执行,在系统空闲时或者缓冲区满的时候再写入物理磁盘,从而提高效率。
  • 系统IO没有缓冲机制,每次执行系统IO操作时都会进行硬盘的读写。

image-20220420172516786

2022.04.20

  • 16
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值