linux系统编程之文件与I/O(二):文件的读取写入

一、read系统调用

一旦有了与一个打开文件描述相关连的文件描述符,只要该文件是用O_RDONLY或O_RDWR标志打开的,就可以用read()系统调用从该文件中读取字节 

函数原型: ssize_t read(int fd, void *buf, size_t count); 参数: fd :想要读的文件的文件描述符 buf : 指向内存块的指针,从文件中读取来的字节放到这个内存块中 count : 从该文件复制到buf中的字节个数 返回值: 如果出现错误,返回-1;读文件结束,返回0;否则返回从该文件复制到规定的缓冲区中的字节数

二、write系统调用

用write()系统调用将数据写到一个文件中 

函数原型: ssize_t write(int fd, const void *buf, size_t count); 函数参数: fd:要写入的文件的文件描述符 buf: 指向内存块的指针,从这个内存块中读取数据写入 到文件中 count: 要写入文件的字节个数 返回值:如果出现错误,返回-1;如果写入成功,则返回写入到文件中的字节个数

三、ioctl 函数

ioctl用于向设备发控制和配置命令,有些命令也需要读写一些数据,但这些数据是不能用read/write读写的,称为Out-of-band数据。也就是说,read/write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。例如,在串口线上收发数据通过read/write操作,而串口的波特率、校验位、停止位通过ioctl设置,A/D转换的结果通过read读取,而A/D转换的精度和工作频率通过ioctl设置。

#include <sys/ioctl.h>

int ioctl(int d, int request, ...); d是某个设备的文件描述符。request是ioctl的命令,可变参数取决于request,通常是一个指向变量或结构体的指针。若出错则返回-1,若成功则返回其他值,返回值也是取决于request。 以下程序使用TIOCGWINSZ命令获得终端设备的窗口大小。

#include <stdio.h>
#include <stdlib.h
#include <unistd.h>
#include <sys/ioctl.h
int main(void)
{
    struct winsize size;
    if (isatty(STDOUT_FILENO) == 0)
        exit(1);
    if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) < 0)
    {
        perror("ioctl TIOCGWINSZ error");
        exit(1);
    }
    printf("%d rows, %d columns\n", size.ws_row, size.ws_col);
    return 0;
}

在图形界面的终端里多次改变终端窗口的大小并运行该程序,观察结果。

四、文件的随机读写

到目前为止的所有文件访问都是顺序访问。这是因为所有的读和写都从当前文件的偏移位置开始,然后文件偏移值自动地增加到刚好超出读或写结束时的位置,使它为下一次访问作好准备。 有个文件偏移这样的机制,在Linux系统中,随机访问就变得很简单,你所需做的只是将当前文件偏移值改变到有关的位置,它将迫使下一次read()或write()发生在这一位置。(除非文件打开时标志有 O_APPEND,在这种情况下,任何write调用仍将发生在文件结束处)

lseek系统调用:

功能说明:通过指定相对于开始位置、当前位置或末尾位置的字节数来重定位,这取决于 lseek() 函数中指定的位置 函数原型:off_t lseek (int  fd,    off_t offset,   int base);

函数参数:

fd:需要设置的文件描述符

offset:偏移量

base:偏移基位置

返回值:返回新的文件偏移值

base 表示搜索的起始位置,有以下几个值:(这些值定义在<unistd.h>)

base 文件位置

SEEK_SET 从文件开始处计算偏移 SEEK_CUR 从当前文件的偏移值计算偏移 SEEK_END 从文件的结束处计算偏移

注意:管道和socket是不能lseek的,否则返回ESPIPE错误(Invalid seek)。

示例程序如下:

/*************************************************************************
    > File Name: file_cp.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 23 Feb 2013 02:34:02 PM CST
 ************************************************************************/
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

int main(int argc, char *argv[])
{
    int infd;
    int outfd;
    if (argc != 3)
    {
        fprintf(stderr, "Usage %s src dest\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    infd = open(argv[1], O_RDONLY);
    if (infd == -1)
        ERR_EXIT("open src error");
    if ((outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
        ERR_EXIT("open dest error");

    char buf[1024];
    ssize_t nread;
    while ((nread = read(infd, buf, 1024)) > 0)
        write(outfd, buf, nread); // 可以调用fsync同步内核缓冲区的数据到磁盘文件
    // 或者打开文件时标志为O_SYNC
    close(infd);
    close(outfd);
    /********************************************************************************************/

    int fd = open("test.txt", O_RDONLY);
    if (fd == -1)
        ERR_EXIT("open error");
    char buf2[1024] = {0};
    int ret = read(fd, buf2, 5);
    if (ret == -1)
        ERR_EXIT("read error");
    ret = lseek(fd, 0, SEEK_CUR); // 从当前位置偏移0个字节
    if (ret == -1)
        ERR_EXIT("lseek");
    printf("current offset=%d\n", ret);

    fd = open("hole.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);
    if (fd == -1)
        ERR_EXIT("open error");
    write(fd, "ABCDE", 5);
    ret = lseek(fd, 1012 * 1024 * 1024, SEEK_CUR);
    if (ret == -1)
        ERR_EXIT("lseek error");
    write(fd, "hello", 5);
    /* 中间的空字符不占用磁盘空间,如ls -lh hole.txt 与 du -h hole.txt
     * 看到的文件大小不一样*/
    close(fd);

    return 0;
}

程序的前部分实现了拷贝文件的基本功能,后部分示例了lseek的用法,因为有些程序需要输入参数,且讨论程序输出结果也比较繁琐,比如上述关于hole.txt文件的实际大小问题,大家可以自己拷贝程序进行测试,印象也更加深刻。

struct stat结构体中的文件长度对应st_size字段,而文件使用的块大小对应st_blksize字段,占用块数对应st_blocks字段。 大部分情况下面,st_size和st_blksize*st_blocks应该是很接近的,除非一种情况就是文件空洞。 一般对应于空洞文件来说,st_size可能很大,而实际占用磁盘空间却很少。

参考:《APUE》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏青枫的专栏

c语言_文件操作_FILE结构体小解释

参考文档来自:https://www.cnblogs.com/haore147/p/3648395.html

941
来自专栏抠抠空间

Django之admin的使用和源码剖析

1570
来自专栏杂烩

MongoDB Java环境下的开发 原

        在项目下建立一个lib文件夹,将下载的驱动包放到lib下并build到path下:

862
来自专栏Android研究院

Android组件化专题 - 路由框架原理

在路由框架之前,我们先了解什么是APT,并实践ButterKnife绑定findById的小功能。为什么先要讲解apt,因为路由的实现apt是核心的代码.看下面...

1321
来自专栏大内老A

ASP.NET MVC下的四种验证编程方式

ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性...

2107
来自专栏Java技术

MyBatis关联映射:一对一、一对多总结一二

(3)用户实体对象,com.xuliugen.mybatis.demo.bean.User

711
来自专栏更流畅、简洁的软件开发方式

分页解决方案 之 分页算法——Pager_SQL的详细使用方法和注意事项

      上一次有点匆忙,如何使用介绍的不是太清楚,而且这两天有改掉了几个bug,所以这次呢详细说一下,然后更新一下代码和demo。       源代码和de...

2496
来自专栏大魏分享(微信公众号:david-share)

实战:应用对持久数据访问| 从开发角度看应用架构9

JPA的API有主要以下几个:实体(entity)、持久性单元(persistence units)、持久性上下文( persistence context)、...

883
来自专栏WindCoder

Logstash的grok表达式与Filebeat的日志过滤

grok为Logstash 的Filter的一个插件,又因为存在表达式要配置,最开始当成过滤条件的配置了。随着深入了解,发现这个只是一个数据结构化转换工具,主要...

1431
来自专栏Java3y

Hibernate【映射】知识要点

前言 前面的我们使用的是一个表的操作,但我们实际的开发中不可能只使用一个表的…因此,本博文主要讲解关联映射 集合映射 需求分析:当用户购买商品,用户可能有多个地...

2917

扫码关注云+社区