文件操作的基本概念参见博客: ​​小何讲Linux: 底层文件I/O操作​​


1.  函数说明

  • open()函数:是用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。
    所谓打开文件实质上是在进程与文件之间建立起一种连接,而“文件描述符”唯一地标识着这样一个连接
  • close()函数:是用于关闭一个被打开的文件。当一个进程终止时,所有被它打开的文件都由内核自动关闭,很多程序都使用这一功能而不显示地关闭一个文件。
  • read()函数:是用于将从指定的文件描述符中读出的数据放到缓存区中,并返回实际读入的字节数。若返回0,则表示没有数据可读,即已达到文件尾。读操作从文件的当前指针位置开始。当从终端设备文件中读出数据时,通常一次最多读一行。
  • write()函数:是用于向打开的文件写数据,写操作从文件的当前指针位置开始。对磁盘文件进行写操作,若磁盘已满或超出该文件的长度,则write()函数返回失败。
  • lseek()函数:是将文件指针定位到相应的位置。它只能用在可定位(可随机访问)文件操作中。管道、套接字和大部分字符设备文件是不可定位的,所以在这些文件的操作中无法使用lseek()调用。


2.  open()函数语法要点
所需头文件
#include <sys/types.h> /* 提供类型pid_t 的定义*/
#include <sys/stat.h>    /*记录文件状态头文件*/
#include <fcntl.h>         /*文件的控制,文件访问方式,读写权限的改变*/
函数原型
int open(const char *pathname, int flags, int perms)

在操作文件之前,首先必须打开文件。open函数的打开原理就是将进程files_struct结构体和文件对象file相关联。

函数原型
int open(const char *pathname, int flags, int perms)

pathname :被打开的文件名(可包括路径名)。

flags :文件打开的方式(用下列一个或多个常数进行或运算构成定义,这些常数在fcntl.h 中定义)
O_RDONLY :以只读方式打开文件
O_WRONLY:以只写方式打开文件
O_RDWR:      以读写方式打开文件

O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三个参数为其设置权限
O_EXCL:如果使用O_CREAT时文件已存在,则返回错误消息。这一参数可测试文件是否存在。此时open是原子操作,防止多个进程同时创建同一个文件
O_NOCTTY:使用本参数时,若文件为终端,则不将此设备分配作为此进程的控制终端
O_TRUNC:若文件已经存在,那么会删除文件中的全部原有数据,并且设置文件大小为0。

O_APPEND:以添加方式打开文件,在打开文件的同时,文件指针指向文件末尾,即将写入的数据添加到文件的末尾。
O_NONBLOCK 如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则该选项将此文件的本次打开操作和后续的I/O操作设置非阻塞方式。
O_SYNC 使每次write都等到物理I/O操作完成

flag参数可通过“| ”组合构成,但前3个标志常量(O_RDONLY、O_WRONLY 以及O_RDWR)不能相互组合。

perms被创建文件的存取权限(仅当创建新文件时才使用)

可以用一组宏定义:S_I(R/W/X)(USR/GRP/OTH)
其中R/W/X分别表示读/写/执行权限
USR/GRP/OTH分别表示文件所有者/文件所属组/其他用户
 perms 是文件的存取权限,既可以用宏定义表示法,也可以用八进制表示法。
例如,S_IRUSR | S_IWUSR表示设置文件所有者的可读可写属性。八进制表示法中600也表示同样的权限
 S_IRWXU-设置文件所有者具有全部权限。

函数返回值

成功:返回文件描述符(返回的文件描述符一定是最小的未用描述符数字)
失败:-1


3.  close()函数语法要点所需头文件
#include <unistd.h>
函数原型
int close(int fd)
函数输入值
fd:文件描述符
函数返回值
0:  成功
-1:出错


4.  read()函数语法要点
所需头文件:#include <unistd.h>
函数原型:ssize_t read(int fd, void *buf, size_t count)
参数:
fd:文件描述符
buf:指向存放读出数据的缓冲区的指针
count:指定读出的字节数
函数返回值
成功:读到的字节数
0:已到达文件尾
-1:出错(如以非阻塞方式打开的文件上无数据可读时)

注意:

  • 有多种情况可使实际读到的字节数少于要求读的字节数:
  • 读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前还有30个字节,而要求读100个字节,则read返回30,下一次再调用read时,它将返回0 (文件尾端)。
  • 当从终端设备读时,通常一次最多读一行。
  • 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
  • 某些面向记录的设备,例如磁带,一次最多返回一个记录。
  • 读操作从文件的当前位移量处开始,在成功返回之后,该位移量增加实际读得的字节数。


5.  write()函数语法要点
所需头文件:#include <unistd.h>
函数原型:ssize_t write(int fd, void *buf, size_t count)
参数:
fd:文件描述符
buf:指向存放待写入数据的缓冲区的指针
count:指定希望读出的字节数
函数返回值
成功:已写的字节数
出错:-1
在写普通文件时,写操作从文件的当前指针位置开始。


6.  lseek()函数语法要点
每个打开文件都有一个与其相关联的“当前文件位移量”。它是一个非负整数,对应从文件开始处计算的字节数,通常,读、写操作都从当前文件位移量处开始,并使位移量增加所读或写的字节数。系统默认:当打开一个文件时,除非指定O_APPEND选择项,否则该位移量被设置为0。
所需头文件
#include <unistd.h>
#include <sys/types.h>
函数原型:off_t lseek(int fd, off_t offset, int whence)
参数
fd:文件描述符
offset:偏移量,需要移动的距离,单位是字节,可正可负(向前移,向后移)

函数原型off_t lseek(int fd, off_t offset, int whence)

参数:
whence:基准位置

  • SEEK_SET:基准位置为文件的开头,新位置为偏移量的大小
  • SEEK_CUR:基准位置为文件指针的当前位置,新位置为当前位置加上偏移量
  • SEEK_END:基准位置为文件的结尾,新位置为文件的大小加上偏移量的大小

函数返回值

成功:文件的当前位移,该位移量用于下一个读或写操作。


出错:-1


lseek操作并不引起任何I/O操作,只是修改内核中的记录(修改的是已打开文件对象的f_pos字段)



7.  实例基本功能:
从一个文件(源文件)中读取最后10KB数据并到另一个文件(目标文件);
在实例中源文件是以只读方式打开;
目标文件是以只写方式打开(可以是读写方式);
若目标文件不存在,可以创建并设置权限的初始值为644,即文件所有者可读可写,文件所属组和其他用户只能读。

/*将文件src_file中的后10个字节的数据拷贝到dest_file文件中去,前提是src_file中已经准备好了数据*/

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

#define BUFFER_SIZE 15 /* 每次读写缓存大小,影响运行效率*/
#define SRC_FILE_NAME "src_file" /* 源文件名*/
#define DEST_FILE_NAME "dest_file" /* 目标文件名文件名*/
#define OFFSET 10 /* 复制的数据大小*/

int main()
{
int src_file, dest_file;
unsigned char buff[BUFFER_SIZE];
int real_read_len;

/* 以只读方式打开源文件*/
src_file = open(SRC_FILE_NAME, O_RDONLY);
if (src_file < 0 )
{
printf("SRC_FILE Open error\n");
exit(1);
}

/* 以只写方式打开目标文件,若此文件不存在则创建该文件, 访问权限值为644 */
dest_file = open(DEST_FILE_NAME,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);

if (dest_file < 0)
{
printf("DEST_FILE_NAME Open error\n");
exit(1);
}

/* 将源文件的读写指针移到最后10KB的起始位置*/
lseek(src_file, -OFFSET, SEEK_END);
/* 读取源文件的最后10KB数据并写到目标文件中,每次读写1KB */
while ((real_read_len = read(src_file, buff, sizeof(buff))) > 0)
{
write(dest_file, buff, real_read_len);
}

close(dest_file);
close(src_file);

return 0;
}


首先编写源文件,在源文件中输入一串字符:(大于10个字节)

小何讲Linux: 基本文件操作和实例_读写文件


在终端中编译运行源程序:

小何讲Linux: 基本文件操作和实例_函数讲解_02


在终端中使用cat命令输出目标文件中的内容:

小何讲Linux: 基本文件操作和实例_基本文件操作_03

打印变量real_read_len,显示的是10个字节:

小何讲Linux: 基本文件操作和实例_程序实例_04

读者可以看见,此处只从源文件拷贝了9个字节的内容到目标文件,我觉得应该是文件末尾的结束符eof占了一个字节,所以导致只显示了9个字节的数据,文件结束符eof为不可打印字符,未显示出来。

本人学识浅薄,如果大家知道其中的原因,还望不惜赐教。