Linux下文件IO操作


title: Linux下IO操作
date: 2019-07-16 15:08:55
tags: Linux
categories: Linux

        在linux下一切皆文件,我们要想操作硬件即是对文件的操作。对文件的操作方法有即读写操作,在这里介绍标准IO的操作主要针对对象为普通文件。标准IO本质是一个标准库,其实现还是建立在系统调用的基础之上,对用户提供统一的接口,标准IO库在不同系统上调用各自系统的系统调用,而对用户的接口不变,这样,用户层编写的代码就可以移植到不同系统,而不做修改,并且不需要关心库内部如何实现。

一、相关概念的解释

1.标准IO库

        本质是一个C库,是一组接口。标准IO本质是一个标准库,其实现还是建立在系统调用的基础之上,对用户提供统一的接口,标准IO库在不同系统上调用各自系统的系统调用,而对用户的接口不变,这样,用户层编写的代码就可以移植到不同系统,而不做修改,并且不需要关心库内部如何实现。系统调用其实是一组接口,通过这组接口可以与内核进行方便交互,然后控制进程调度,内存管理,文件操作等。

2.流的含义

        当在调用 标准IO 中的接口 来打开文件,操作系统会产生一个结构体指针(与打开文件进行关联),操作这个结构体指针,就在操作相应文件,把这个结构体指针称作 指针流。 ----> FILE *

3.缓冲区

①概念

        当我们在调用标准IO的函数的时候,操作系统从用户态切换到内核态,然后在执行相应的命令,执行完毕之后,将结果返回到用户态,这个时候又得从内核态切换到用户态,这样来回切换,加大了操作系统的开销,为了减少这种情况的发生,则设立缓冲机制,直接将所要操作的内容先读到缓冲区当中,再需要的的时候从缓冲区读到内核当中。减少了用户态与内核直接切换的情况。

        使用缓冲区的目的: 减少系统的开销,提高系统的运行效率

②分类

        1.全缓冲:当缓冲区写满,或者遇到 \n,或者强制性刷新缓冲区(fflush),缓冲的内容会被输出 。

        2.行缓冲:按行读写,当缓冲区遇到 \n 或强制性刷新(fflush),会刷新缓冲区的内容 。

        3.不缓冲:错误码 不经过缓冲区 ,直接打印到终端 。

二、标准IO相关函数

1.fopen打开/创建文件

函数原型FILE * fopen(const char *path, const char *mode)
参数1path:要打开的文件路径
参数2mode:以什么方式打开
r 以读的方式打开文件,文件必须是存在的
r+ 以读写的方式来打开文件,文件必须存在
w 以写的方式打开文件,如果文件不存在,则创建,如果文件存在,清0。
w+ 以读写的方式来打开文件,同w 一致。
a 以追加的方式来打开文件 ,(文件的末尾位置)
a+ 以追加的方式来打开文件(读写权限)(文件的末尾位置)
返回值成功:指向文件对象的指针流
失败:NULL,并设置error值

2.fclose关闭文件

函数原型int fclose(FILE *stream)
功能关闭打开的文件
参数stream:指针流,即fopen函数的返回值
返回值成功:0
失败: EOF

3.feof判断文件是否结束

原型int feof(FILE *stream)
功能判断文件操作是否结束(操作到文件的末尾位置)
参数stream:指针流(要判断的文件)
返回值0: 文件未结束
非0:文件结束
常见使用方式if、while判断

4.fread按块读(常用)

原型size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
功能将stream 指针指向的文件内容读到 ptr 缓冲区当中 一次读nmemb块,每块的大小是size
参数ptr: 缓存区的首地址
size: 块的大小
nmemb:块数
stream: 指针流
返回值成功:读到的块数
失败:负数
0:末尾位置

5.fwrite按块写(常用)

原型size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
功能将ptr中的内容写到stream中,一次写memb块,每块的大小size
参数ptr: 缓冲区的首地址
size: 块的大小
memb: 块数
stream:指针流
返回值成功:写的块数
失败:负数

6.小实验

小应用:

#include <stdio.h>

int main()
{
	size_t  ret;
    //创建buf缓冲区
	char buf[6] = {"hello"};
	char buf_d[6] = {'0'};
	//创建hello.data文件
	FILE * p = fopen("hello.data","w+");
	if(NULL == p)
	{
		printf("open is error\r\n");
		return -1;
	}
	//向文件写内容
	ret = fwrite(buf,5,1,p);
	if(ret < 0)
	{
		printf("fwrite is error\r\n");
		return -1;
	}
	
	//将文件的读写位置移动到起始位置
	fseek(p,0,SEEK_SET);
	
	//将文件中的读出 缓冲中
	ret = fread(buf_d,5,1,p);
	if(ret < 0)
	{
		printf("fread is error\r\n");
		return -1;
	}
	printf("fread:%s\r\n",buf_d);
	fclose(p);
	return 0;
}

在这里插入图片描述

利用上面几个命令实现文件的拷贝:

将当前目录下的1.png图片拷贝一份放在当前目录下的2.png中

#include <stdio.h>
#include <string.h>

#define N 20

int main(void)
{
    int ret;
	char  buf[N] = {'0'};
	//源文件
	FILE * p = fopen("./1.png","r+");       //1.png存在,读取它
	FILE * q = fopen("./2.png","w+");       //2.png不存在,创建它
	if(NULL == p|NULL == q)
	{
		printf("open is error\r\n");
		return -1;
	}
	while(1)
	{
		ret = fread(buf,1,1,p);
		if(ret < 0)
		{
			printf("fread is error\r\n");
			return -1;
		}
        else if(ret == 0)
		{
			
			printf("read finshed \r\n");
			break;
		}
		
		printf("reading...............\r\n");
		
		ret = fwrite(buf,1,1,q);
		if(ret < 0)
		{
			printf("fwrite is error\r\n");
			return -1;
		}
		
	}	
	
	fclose(p);	
	fclose(q);
	return 0;
}

7、fgetc按字节读

原型int fgetc(FILE *stream)
参数stream:指针流
返回值成功:从unsigned char类型转换到 int型的字符
失败:如果 操作 失败或到达文件末尾,则返回 EOF: -1

8、fputc按字节写

原型int fputc(int c, FILE *stream)
参数c:fgetc的返回值
stream:指针流
返回值成功:函数返回写入文件的字符的ASCII码值
失败:返回EOF(-1)

9、fgets按行读

原型char *fgets(char *s, int size, FILE *stream)
功能从 stream流中读取多至 size - 1 个字符, 保存在 s 指向的缓冲区
参数s : 缓冲区的首地址
size: size-1个字节
stream: 指针流
返回值成功:char * 类型的指针
失败:NULL
注意读到 换行符(newline) 或 EOF 时操作结束, 如果读到的是换行符, 把换行符也保存在缓冲区 中. 函数将在 最后一个 字符后面添加一个 ’\0’ 字符.

10、fputs按行写

原型int fputs(const char *s, FILE *stream)
参数s: 缓冲区的首地址
stream: 指针流
返回值成功:>=0的值
失败:-1

11、fprintf格式化输出

函数原型int fprintf(FILE *stream, const char *format, …)
功能描述按一定的格式输入内容到stream指针指向的文件中
参数stream:指针流
format: 格式(printf格式,比如:%d,%s等)
…: 可变参数
返回值成功:字节数
失败:负数并设置errno
举例ret = fprintf(p,"%s %d",str,a);

12、fscanf格式化输入

函数原型int fscanf(FILE *stream, const char *format, …)
参数stream:指针流
format: 格式(参照scanf格式)
…: 可变参数
返回值成功:参数的个数
失败:负数

13、fseek文件定位

函数原型int fseek(FILE *stream, long offset, int whence)
功能改变文件读写位置
参数stream:指针流
offset:偏移量
whence:基准
SEEK_SET:从文件起始位置开始偏移
SEEK_CUR:从文件当前位置开始偏移
SEEK_END:从文件末尾位置开始偏移
返回值成功:0
失败:-1

14、ftell获得文件流当前的读写位置

原型long ftell(FILE *stream)
相当于fseek(stream,OFFSET,SEEK_CUR)

15、rewind复位文件指示器到文件头

原型void rewind(FILE *stream)
相当于fseek(stream,0,SEEK_SET)

三、内核IO相关函数

1、open打开/创建文件

函数原型int open(const char *pathname,int flags,mode_tmode);
函数功能若文件存在则打开,若文件不存在则创建
参数pathname:要打开文件的路径及名字
flags:
O_RDONLY 以只读方式打开文件
O_WRONLY 以只写方式打开文件
O_RDWR 以可读写方式打开文件
O_CREAT 创建一个文件
O_TRUNC 打开文件,会把已经存在的内容删除
O_APPEND 追加方式打开文件
mode_tmode:创建文件的权限
返回值成功:文件描述符,它是一个非负的正整数,即文件ID号
失败:-1

2、close关闭文件

函数原型int close(int fd)
函数功能用于关闭一个被打开的的文件
参数fd:文件描述符
返回值成功:0
失败:-1,并设置错误号

3、write写函数

函数原型ssize_t write(int fd, const void *buf, size_t nbyte);
参数fd: 向哪个文件中写(文件描述符来自open函数的返回值)
buf: 缓冲区的首地址,即写的内容
nbyte:要写入文件指定的字节数
返回值成功:实际写的字节数
失败:-1

4、read读函数

函数原型ssize_t read (int fd, void *buf, size_t count);
参数fd: 读哪个文件(文件描述符来自open函数的返回值)
buf: 读缓冲区的首地址,把读到的文件放哪
count:要读多少个字节
返回值成功:读取的字节数
失败:-1,并设置errno
如果在调read之前已到达文件末尾,则这次read返回0。

5、lseek调整读写位置指针

函数原型off_t lseek(int fd, off_t offset, int fromwhere)
参数fd: 文件描述符,要操作哪一个文件
offset: 偏移量,相对于第三个参数的偏移量,单位字节,可正可负
fromwhere:基准位置
SEEK_SET: 将读写位置指向文件头后再增加offset个位移量。
SEEK_CUR: 以目前的读写位置往后增加offset个位移量。
SEEK_END :将读写位置指向文件尾后再增加offset个位移量。
返回值成功:返回目前的读写位置,也就是距离文件开头多少个字节。
失败:返回-1,errno 会存放错误代码

四、目录IO相关函数

1.opendir目录打开

函数原型DIR *opendir(const char *payhname)
参数payhname:打来的目录以及路径
返回值成功:返回目录指针流
失败:返回NULL

2.mkdir创建目录

函数原型int mkdir(const char * path,mode_t mode)
参数path:创建的目录文件路径
mode:该目录的访问权限
返回值成功:0
失败:-1

3.closedir关闭目录

函数原型int closedir(DIR *dir)
参数dir:目录指针流
返回值成功:0
失败:-1

4.readdir读取目录

读取到的是当前目录下的子文件和子目录的相关信息
  linux中当前目录下的子文件和子文件夹以链表的形式存放,如果我们只调用一次这个函数只会读到链表的头,即当前目录下的一个文件/文件夹。因此我们要用while循环遍历当前目这个链表。来读取当前目录下所有子文件/目录的信息。

函数原型struct dirent* readdir(DIR* dir_handle);
参数dir_handle:目录指针流
返回值成功:struct dirent结构体指针
失败:若在目录尾或出错返回NULL

此结构体应至少包含下列两个指针,该结构体定义在#include<dirent.h>中
struct dirent
{
ino_t d_ino; //inode号
char d_name[NAME_MAX+1]; //文件名
}

5.重置读取目录的流指针

①、rewinddir重置读取目录的位置未开头

当前目录下的子文件和子目录以链表形式存放,即把指针流移到链表的头。

*void rewinddir(DIR dp)

参数:目录指针流

②、telldir获取当前流指针的位置

*long int telldir(DIR dir);

参数:目录指针流

返回值:返回值记录着一个目录流的当前位置。此返回值代表距离目录文件开头的偏移量返回值返回下个读取位置,有错误发生时返回-1。

③、seekdir设置目录流指针的读取位置

*void seekdir(DIR dirp,long int loc);

参数1:目录流指针

参数2:距离目录文件开头的偏移量

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值