linux文件I/O之 close()、lseek()、read()、write() 函数用法

 1. close() 函数

头文件和函数声明

#include <unistd.h>

int close(int fd);

函数功能

关闭一个文件描述符

返回值

成功时返回 0。失败则返回 -1,并设置 errno 为相应的错误标志。

参数

fd:文件描述符

说明

像其它所有系统调用一样,应对 close() 的调用进行错误检查,如下所示:

if (-1 == close(fd)) {

        perror("close error");

        exit(EXIT_FAILURE);

}

以上代码能够捕获的错误有: 

  • 企图关闭一个未打开文件描述符。
  • 多次关闭同一个文件描述符。
  • 捕获特定文件系统在关闭操作时诊断出的错误,例如:NFS(网络文件系统),如果 NFS 出现提交失败,这意味着数据没有抵达远程磁盘,随之将这一错误作为 close() 调用失败的原因传递给应用程序。

例子

以下程序功能:打开 "./log.txt" 文件,然后调用 close() 函数关闭打开的文件

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

int main(int argc, char *argv[])
{
    int flags = O_WRONLY | O_CREAT | O_APPEND; /* 只写,没有该文件则创建,写时从文件内容的末尾附加新内容 */
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* rw-rw-rw- */
    int fd = open("./log.txt", flags, mode); 
    if (fd < 0) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    int ret = close(fd); /* 关闭文件描述符 */
    if (ret < 0) {
        perror("close error");
        exit(EXIT_FAILURE);
    }

    printf("process exit success\n");

    return 0;
}

 2. lseek() 函数

        每个打开的文件都有一个与其相关联的当前文件偏移量(current file offset),它通常是一个非负整数,用以度量从文件开始处计算的字节数。

        读写操作都从当前文件偏移量开始,并使偏移量增加所读写的字节数。

        系统默认的情况,当打开一个文件时,除非指定 O_APPEND 选项打开,否则该偏移量被设置为 0。可以调用 lseek() 函数为一个打开的文件设置偏移量。 

头文件和函数声明

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

typedef long off_t;

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

函数功能

为一个打开的文件设置偏移量

返回值

成功返回新的文件偏移量,失败返回 -1,并设置 errno 为相对应的错误标志。(注意:成功返回新的文件偏移量可能为负数,所以在判断 lseek() 函数一定要谨慎,应该判断返回值是否等于 -1

参数

fd:文件描述符

offset:参数 offset 的含义跟参数 whence 的值有关

  • whence = SEEK_SET 时,则将文件的偏移量设置为文件开始处 offset 个字节,参数 offset 必须为非负数。
  • whence = SEEK_CUR 时,则将文件的偏移量设置为 当前文件的偏移量 + offset 个字节,参数 offset 可以为正数,也可以为负数。
  • whence = SEEK_END 时,则将文件的偏移量设置为 文件长度 + offset 个字节,参数 offset 可以为正数,也可以为负数。

off_t new_offset = lseek(fd, 0, SEEK_SET);         // 将 fd 引用的文件的文件偏移量设置到文件开始处

off_t new_offset = lseek(fd, 0, SEEK_END);        // 将 fd 引用的文件的文件偏移量设置到文件尾部

off_t new_offset = lseek(fd, -1, SEEK_END);       // 将 fd 引用的文件的文件偏移量设置到文件尾部的前一个字节处

 linux 内核中对 SEEK_SET、SEEK_CUR、SEEK_END 的定义如下所示:

#define SEEK_SET	0	/* seek relative to beginning of file */
#define SEEK_CUR	1	/* seek relative to current file position */
#define SEEK_END	2	/* seek relative to end of file */

例子

以下程序功能:打开 "./log.txt" 文件,调用 lseek() 函数将文件偏移量设置为文件尾部,然后从文件尾部开始写入 “hello world\n” 字符串,最后调用 close() 函数关闭打开的文件

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

int main(int argc, char *argv[])
{
    int flags = O_WRONLY | O_CREAT; /* 只写,没有该文件则创建,文件偏移量默认为 0 */
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* rw-rw-rw- */
    int fd = open("./log.txt", flags, mode); 
    if (fd < 0) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    off_t new_offset = lseek(fd, 0, SEEK_END); // 将文件偏移量设置为文件尾部,也可用于获取文件长度(文件所占字节数)的方法
    if (-1 == new_offset) {
        perror("lseek error");
        exit(EXIT_FAILURE);
    }
    off_t length = new_offset;
    printf("before write, file length = %ld\n", length);
    
    char buf[] = "hello world\n"; // strlen(buf) = 12; sizeof(buf) = 13
    ssize_t nwrite = write(fd, buf, strlen(buf));
    if (nwrite < 0) {
        perror("write error");
        exit(EXIT_FAILURE);
    }
    length = lseek(fd, 0, SEEK_END);
    if (-1 == length) {
        perror("lseek error");
        exit(EXIT_FAILURE);
    }
    printf("after write, file length = %ld\n", length);

    if (-1 == close(fd)) {
        perror("close error");
        exit(EXIT_FAILURE);
    }

    return 0;
}

3. read() 函数

头文件和函数声明

#include <unistd.h>

/********************/

/* 32位系统 */

typedef unsigned int size_t ;

typedef int ssize_t ;

/* 64位系统 */

typedef unsigned long size_t ;

typedef long ssize_t;

/********************/

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

函数功能

从文件描述符 fd 引用的文件中请求读取 count 字节的数据并存储到从 buf 开始的缓冲区中

返回值

  • 如果 read() 成功,将返回实际读取到的字节数。
  • 如果遇到文件结尾(EOF)则返回 0。
  • 如果读取失败,则返回 -1,并设置 errno 为相应的错误标志。

说明

一次 read() 调用所读取到的字节数可能会小于请求的字节数

  • 对于普通文件而言,这可能读取到靠近文件尾部了。
  • 对于其它文件类型,比如管道、FIFO、socket 或终端,在不同环境下也会出现读取到的字节数小于请求的字节数。例如,默认情况下,从终端读取字符,遇到换行符(\n),read() 调用就会返回。

参数

fd:文件描述符

buf:存储读取到的数据的首地址

count:请求读取 count 字节数据

4. write() 函数

头文件和函数声明

#include <unistd.h>

/********************/

/* 32位系统 */

typedef unsigned int size_t ;

typedef int ssize_t ;

/* 64位系统 */

typedef unsigned long size_t ;

typedef long ssize_t;

/********************/

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

函数功能

向文件描述符 fd 引用的文件中写入从 buf 开始的缓冲区中 count 字节的数据。

返回值

成功时返回被写入的字节数(若为 0 则表示没有写入数据)。失败则返回 -1,并设置 errno 为相应的错误标志。

参数

fd:文件描述符

buf:要被写入文件的数据在内存中的首地址

count:请求写入 count 字节

说明

如果 write() 调用成功,将会返回实际写入文件的字节数。该返回值可能会小于 count 参数值,这被叫做部分写。

  • 对于磁盘文件来说,造成部分写的原因可能是磁盘已满,或是因为进程资源对文件大小的限制
  • 对于磁盘文件执行 I/O 操作时,write() 调用成功并不能保证已经完成写入磁盘,因为为了减少磁盘活动量和加快 write() 系统调用,内核会缓存磁盘的 I/O 操作

例子

以下程序功能:拷贝一个普通文件。命令行格式:./copyfile_demo log.txt log2.txt

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

#define BUF_SIZE 4096

int main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("%s input_file out_put_file\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int ifd = open(argv[1], O_RDONLY); // 只读方式打开
    if (-1 == ifd) {
        fprintf(stderr, "open %s error: %s\n", argv[1], strerror(errno));
        exit(EXIT_FAILURE);
    }

    int flags = O_WRONLY | O_CREAT | O_EXCL; // 只读,创建新文件,O_EXCL:此标志,保证 open() 调用只能创建新文件,如果文件存在则报错
    int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* rw-rw-rw- */
    int ofd = open(argv[2], flags, mode);
    if (-1 == ofd)
    {
        if (-1 == close(ifd))
            perror("close error");

        fprintf(stderr, "open %s error: %s\n", argv[2], strerror(errno));
        exit(EXIT_FAILURE);
    }

    char buf[BUF_SIZE] = "";
    ssize_t nread = 0;
    while ((nread = read(ifd, buf, BUF_SIZE)) > 0) {
        if (write(ofd, buf, nread) != nread) {
            if (-1 == close(ifd))
                perror("close error");

            if (-1 == close(ofd))
                perror("close error");

            perror("write error");
            exit(EXIT_FAILURE);
        }
    }

    // 成功 read() 完整个文件,nread = 0
    if (-1 == nread) {
        if (-1 == close(ifd))
            perror("close error");

        if (-1 == close(ofd))
            perror("close error");

        perror("read error");
        exit(EXIT_FAILURE);
    }

    if (-1 == close(ifd))
        perror("close error");

    if (-1 == close(ofd))
        perror("close error");

    printf("copy %s success\n", argv[1]);

    return 0;
}

 注:对实例代码中 open() 函数有疑惑的小伙伴,欢迎浏览我的这篇博文,这里就不再过多阐述。

linux文件I/O之 open() 函数用法_微尘8的博客-CSDN博客

参考:

《UNIX环境高级编程》(第3版)

《Linux-UNIX系统编程手册》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值