文件的输入/输出操作

1.文件I/O操作概述

在Linux下,文件I/O操作可分为两类,一类是基于文件描述符的I/O操作,另一类是基于数据流的I/O操作。我们可以先来了解文件描述符和数据流这些基本概念。

1.1 文件描述符简介
所谓的文件描述符,就是进程与打开的文件的一个桥梁。通过这个桥梁,才可以在进程中对这个桥梁进行操作。
在Linux环境下,每打开一个磁盘文件,都会在内核中建立一个文件表项,文件表项里存储着文件的状态信息、存储文件内容的缓冲区和当前文件的读写位置。如果同一个磁盘文件打开了三次,就会创建3个这样的文件表项(a,b,c),读写该文件时,只会改变文件表项中的读写位置。这3个文件表项存储在一个文件表数组table[3]中,其中table[0] = a, table[1] = b, table[2] = c。这个文件表的下标就称之为文件描述符。将这个文件描述符存储在一个数组des[3] = {0,1,2},那么,在进程中就可以通过这个des数组下标引用文件表项。也就是说,通过文件描述符就可以访问到这个磁盘文件。

概括地说,文件描述符就是一个小整数:分别是标准输入0,标准输出1,标准错误输出2.它们对应的物理设备是键盘、显示器、显示器。

画个图帮大家理解:
在这里插入图片描述

1.2 数据流概述
从数据操作方式这个角度来说,Linux系统中的文件(普通文件与设备文件)可以看做是数据流。对文件操作之前,必须先调用标准I/O库函数fopen()将数据流打开。打开后,就可以对数据流进行输入和输出操作。
要对数据流进行读写操作,需要标准I/O库函数和FILE类型的文件指针一起来实现。这个文件指针是打开数据流时返回的指针,该指针用来表示要操作的数据流。当执行程序时,有3个数据流不需要进行特定的函数进行打开操作,它们会自动打开。这3个数据流是标准输入、标准输出、和标准错误输出。它们自动打开,当不使用时,也会自动关闭。
然而,调用标准I/O库函数fopen()打开数据流,在对数据流进行操作后,需要调用fclose()函数将其关闭。fclose()函数在关闭数据流之前,会清空在操作过程中分配的缓冲区并保存数据信息。

2.基于文件描述符的I/O操作

基于文件描述符的这些I/O操作,都是Linux操作系统提供的一组文件操作的接口函数,如open(),close(),read(),write(),lseek()等。

2.1 文件的打开与关闭
要对一个文件进行操作,前提是它已经存在,然后才能打开。打开后就可以对其操作或控制。在操作完成后,需要将其关闭,如果不及时关闭,就可能造成文件中数据的丢失。
在Linux中,提供了系统调用函数open(),close()用于打开和关闭一个已经存在的文件。

2.1.1 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);
int creat(const char* pathname, mode_t mode);
//函数具体使用哪个,和具体应用场景有关。
//如果目标文件不存在,需要open()创建,则第三个参数创建文件的默认权限。
//否则使用有两个参数的open()函数。

上述的三个函数在调用成功时,都会返回其新分配的文件描述符;否则返回值为-1,并设置适当的errno值。
参数:
pathname均代表要打开或创建的这个文件的路径名称;
flags代表文件打开方式的宏定义;
(O_RDONLY: 只读打开;)
(O_WRONLY: 只写打开;)
(O_RDWR: 读写打开;)
(O_CREAT: 若文件不存在,则创建。需要使用mode选项来指明新文件的访问权限;)
(O_APPEND: 追加写)
mode均代表文件的访问权限。

2.1.2 close()函数
该函数用于关闭一个已经打开的文件,其定义如下:

#include <unistd.h>
int close(int fd);

如果调用成功,则返回0;失败,返回-1,并设置适当的errno值。
参数fd是要关闭的文件描述符。

2.2 文件的读写操作
在Linux系统中,提供了系统调用函数read()和write(),用于实现文件的读写操作。

2.2.1 read()函数
该函数从打开的文件(包括设备文件)中读取数据,该函数定义如下:

#include <unistd.h>
ssize_t read(int fd, void* buf, size_t count);

参数:
fd代表的是要进行读写的文件的文件描述符;
buf代表的是读取的数据存放在buf指针所指向的缓冲区中;
count代表的是读取的数据的字节数。
读取文件数据时,文件的当前读写位置会向后移动。
注意:这个读写位置和使用C标准I/O库时的读写位置有可能不同。这个读写位置是记在内核中的,而使用C标准I/O库时的读写位置是用户空间I/O缓冲区的位置。
如果调用成功,返回值为读取的字节数;否则返回值-1,并设置适当的errno值。

2.2.2 write()函数
该函数向打开的设备或文件中写入数据,其定义如下:

#include <unistd.h>
ssize_t write(int fd, const void* buf, size_t count);

参数:
fd代表想要写入数据的文件的文件描述符;
buf指向写入文件的数据的缓冲区;
count代表写入文件的数据的字节数。
调用成功返回写入的字节数,否则返回-1,并设置适当的errno值。
说明:当向常规文件写入数据时,返回值是字节数count; 但是当向终端设备或者网络中写入时,返回的不一定为写入的字节数。

2.2.3 文件的定位
每个打开的文件都记录着当前的读写位置,打开文件时读写位置是0,表示文件开头,通常读写多少个字节就会将读写位置往后移动多少个字节。

lseek()函数可以移动当前的读写位置,通常也叫做偏移量,定义如下:

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

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

参数:
fileds代表文件名描述符;
offset代表偏移量;
whence代表用于偏移时的相对位置,其取值如下:
(SEEK_SET: 从文件开头计算偏移量;)
(SEEK_CUR: 从当前位置计算偏移量;)
(SEEK_END: 从文件末尾计算偏移量;)
偏移量允许超过文件末尾,这种情况下对该文件的下一次写操作将延长文件,未写入内容的空间用’\0’填满。
函数调用成功返回新的偏移量,否则返回-1,并设置新的errno值。

例:通过调用上述的几种系统调用函数,对文件进行简单的读写操作。

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

int main()
{
    char* path = "/root/file/oldfile.txt"; /*进行操作的文件路径*/
    int fd;
    char buf[40], buf2[]="hello mrcff"; /*自定义读写用的缓冲区*/
    int n, i;
    if((fd = open(path, O_RDWR))<0)
    {
	perror("open file failed!");
	return 1;
    }
    else
	printf("open file successful!\n");
    if((n = read(fd, buf, 20))<0)
    {
	perror("read failed!");
	return 1;
    }
    else
    {
	printf("output read data:\n");
	printf("%s\n", buf); /*将读取的数据输出到终端控制台*/
    }
    if((i = lseek(fd, 11, SEEK_SET))<0) /*定位到从文件开头处到第11个字节处*/
    {
	perror("lseek error!");
	return 1;
    }
    else
    {
	if(write(fd, buf2, 11)<0)  /*向文件中写入数据*/
	{
	    perror("write error!");
	    return 1;
	}
	else
	{
	    printf("write successfully!\n");
	}
    }
    close(fd); /*关闭文件的同时保存对文件的改动*/

    if((fd = open(path, O_RDWR))<0)   /*打开文件*/
    {
	perror("open file failed!");
	return 1;
    }
    if((n = read(fd, buf, 40)) <0)
    {
	perror("read 2 error!");
	return 1;
    }
    else
    {
	printf("read the changed data:\n");
	printf("%s\n", buf);  /*将数据输出到终端*/
    }
    if(close(fd)<0) /*关闭文件*/
    {
	perror("close failed!");
	return 1;
    }
    else
	printf("close successfully! Bye~\n");
    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值