文件I/O实践(1) --基础API

简介: 什么是I/O输入/输出是内存和外设之间拷贝数据的过程:   设备->内存: 输入操作   内存->设备: 输出操作 高级I/O: ANSI C提供的标准I/O库函数成为高级I/O, ...

什么是I/O

输入/输出是内存和外设之间拷贝数据的过程:

   设备->内存: 输入操作

   内存->设备: 输出操作


 高级I/O: ANSI C提供的标准I/O库函数成为高级I/O, 也称为带缓冲的I/O;

 低级I/O: Linux 提供的系统调用通常也称为不带缓冲的I/O;

 

文件描述符

  对于Linux内核而言, 所有的文件或设备都对应一个文件描述符(Linux的设计哲学: 一切皆文件), 这样可以简化系统编程的复杂程度;

  当打开/创建一个文件的时候, 内核向进程返回一个文件描述符(是一个非负整数). 后续对文件的操作只需通过该文件描述符即可进行, 内核记录有关这个打开文件的信息;

  一个进程启动时, 默认已经打开了3个文件, 标准输入(0, STDIN_FILENO), 标准输出(1, STDOUT_FILENO), 标准错误输出(2, STDERR_FILENO), 这些常量定义在unistd.h头文件中; 

  其中, 文件描述符基本上是与文件描述指针(FILE*)一一对应的, 如文件描述符0,1,2 对应 stdin, stdout, stderr;

 

文件指针与文件描述符的转换

fileno: 将文件指针转换成文件描述符

       int fileno(FILE *stream);

fdopen: 将文件描述符转换成文件指针

       FILE *fdopen(int fd, const char *mode);

//示例
int main()
{
    cout << "fileno(stdin) = " << fileno(stdin) << endl;
    cout << "fileno(stdout) = " << fileno(stdout) << endl;
    cout << "fileno(stderr) = " << fileno(stderr) << endl;
    return 0;
}

文件I/O API

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);

参数:

   pathname:  文件名, 可以包含[绝对/相对]路径名;

   flags: 文件打开模式;

   mode: 用来指定对文件所有者, 文件用户组以及系统中的其他用户的访问权限;

注意: newMode = mode & ~umask

 

flags常用值


//示例1
int main()
{
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1)
    {
        cerr << "file open error, errno = " << errno <<
             "\nstrerror: " << strerror(errno) << endl;
        perror("perror");
        exit(EXIT_FAILURE);
    }
    cout << "file open success" << endl;
}
//示例2
inline void err_exit(std::string message)
{
    perror(message.c_str());
    exit(EXIT_FAILURE);
}

int main()
{
    umask(0000);
    int fd = open("test.txt", O_RDWR|O_CREAT|O_EXCL, 0666);
    if (fd == -1)
        err_exit("file open error");
    else
        cout << "file descriptor = " << fd << endl;
}

[附]

(1). umask API

   //改变umask值

   mode_t umask(mode_t mask);

(2). ulimit -a

   查看系统中的各种限制;

   其中-n: 查看一个进程所能够打开的最大文件数

(3). cat /proc/sys/fs/file-max 

   查看一个系统能够支持的最大打开文件数(该数与内存大小有关)


2.close

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

关闭文件描述符, 使得文件描述符得以重新利用

 

3.read

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

返回值:

  错误: -1

  到达文件尾: 0

  成功: 返回从文件复制到规定缓冲区的字节数

 

4.wirte

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

返回值:

   错误: -1

   什么都没做: 0

   成功: 返回成功写入文件的字节数

 

注意:

   write返回大于0时, 并不代表buf的内容已经写入到磁盘上的文件中了, 其仅仅代表buf中的数据已经copy到相应的内核缓冲区了. 要实现将缓冲区的内容真正”冲洗”到磁盘上的文件, 需要调用fsync函数;

     int fsync(int fd);

   其将内核缓冲区中尚未写入磁盘的内容同步到文件系统中;

   其实在open调用的时候也可以指定同步选项:O_SYNC  O_SYNC The file is opened for synchronous I/O.   Any  write(2)s  on  the  resulting  file  descriptor will block the calling process until the data has been physically written to the underlying hardware.

   write会等到将buf的内容真正的写入到磁盘才真正返回;

//示例: 带有O_SYNC选项
int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        cerr << "Usage : " << argv[0] << " src dest" << endl;
        exit(EXIT_FAILURE);
    }

    int infd = open(argv[1], O_RDONLY);
    if (infd == -1)
        err_exit("file O_RDONLY error");
    int outfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, 0666);
    if (outfd == -1)
        err_exit("file O_WRONLY error");

    char buf[1024];
    int readBytes, writeBytes;
    while ((readBytes = read(infd, buf, sizeof(buf))) > 0)
    {
        writeBytes = write(outfd, buf, readBytes);
        cout << "readBytes = " << readBytes
             << ", writeBytes = " << writeBytes << endl;
    }
}

文件的随机读写

5.lseek

对应于C库函数中的fseek, 通过指定相对于当前位置, 末尾位置或开始位置的字节数来重定位currp:

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

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

 

Whence取值:

SEEK_SET

   The offset is set to offset bytes.

SEEK_CUR

   The offset is set to its current location plus offset bytes.

SEEK_END

   The offset is set to the size of the file plus offset bytes.

//示例1
int main(int argc, char *argv[])
{
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1)
        err_exit("open error");
    char buf[1024] = {0};
    int readBytes = read(fd, buf, 5);
    cout << "readBytes = " << readBytes << ", buf: " << buf << endl;
    int seekCount = lseek(fd, 0, SEEK_CUR);
    cout << "current offset = " << seekCount << endl;
}
//示例2: 产生空洞文件
int main(int argc, char *argv[])
{
    int fd = open("hole.txt", O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0666);
    if (fd == -1)
        err_exit("open error");

    if (write(fd, "ABCDE", 5) == -1)
        err_exit("first write error");
    //创建一个1G的文件
    if (lseek(fd, 1024*1024*1024, SEEK_CUR) == -1)
        err_exit("lseek error");
    if (write(fd, "Hello", 5) == -1)
        err_exit("second write error");
    close(fd);
}

[附]

-查看hole.txt文件

  od -c hole.txt

  cat -A hole.txt

-查看该文件大小

  du -h hole.txt

  du -b hole.txt

  du -k hole.txt

  du -m hole.txt

 

目录访问

6.opendir

       #include <sys/types.h>
       #include <dirent.h>
       DIR *opendir(const char *name);

返回值:

   成功: 返回目录指针;

   失败: 返回NULL;

 

7.readdir

struct dirent *readdir(DIR *dirp);

返回值:

   成功: 返回一个指向dirent结构的指针, 它包含指定目录的下一个连接的细节;

   没有更多连接时, 返回0;

struct dirent
{
    ino_t          d_ino;       /* inode number */
    off_t          d_off;       /* not an offset; see NOTES */
    unsigned short d_reclen;    /* length of this record */
    unsigned char  d_type;      /* type of file; not supported
                                         by all filesystem types */
    char           d_name[256]; /* filename */
};

8.closedir: 关闭目录   

int closedir(DIR *dirp);
//示例: 简单的ls程序
int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        cerr << "Usage : " << argv[0] << " <directory>" << endl;
        exit(EXIT_FAILURE);
    }

    DIR *dir = opendir(argv[1]);
    if (dir == NULL)
        err_exit("opendir error");

    struct dirent *ent;
    while ((ent = readdir(dir)) != NULL)
    {
        //过滤掉隐藏文件
        if (ent->d_name[0] == '.')
            continue;
        cout << ent->d_name << "\ti-node: " << ent->d_ino
             << ", length: " << ent->d_reclen << endl;
    }
    closedir(dir);
}

9.mkdir

int mkdir(const char *pathname, mode_t mode);

10.rmdir: 删除空目录

int rmdir(const char *pathname);

11. Chmod, fchmod更改权限

int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

12.chown,fchown更改文件所有者/所属组

int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);

目录
相关文章
|
15天前
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
12天前
|
监控 负载均衡 应用服务中间件
探索微服务架构下的API网关设计与实践
在数字化浪潮中,微服务架构以其灵活性和可扩展性成为企业IT架构的宠儿。本文将深入浅出地介绍微服务架构下API网关的关键作用,探讨其设计原则与实践要点,旨在帮助读者更好地理解和应用API网关,优化微服务间的通信效率和安全性,实现服务的高可用性和伸缩性。
31 3
|
16天前
|
前端开发 API 数据安全/隐私保护
打造高效后端API:RESTful设计原则与实践
【9月更文挑战第4天】在数字化时代的浪潮中,后端API作为连接数据和前端的桥梁,其设计质量直接影响着应用的性能和扩展性。本文将深入探讨RESTful API的设计哲学、核心原则以及如何在实际开发中应用这些原则来构建一个高效、易于维护的后端系统。我们将通过代码示例,揭示如何将理论转化为实践,从而为开发者提供一条清晰的道路,去创造那些能够在不断变化的技术环境中茁壮成长的API。
|
13天前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
20天前
|
存储 JSON API
构建高效后端API:实践与思考
【8月更文挑战第31天】本文深入探讨如何打造一个高效、可靠的后端API。我们将通过实际案例,揭示设计原则、开发流程及性能优化的关键步骤。文章不仅提供理论指导,还附带代码示例,旨在帮助开发者构建更优的后端服务。
|
20天前
|
XML JSON API
构建高效后端API:RESTful设计原则与实践
【8月更文挑战第31天】在数字化浪潮中,后端API成为连接世界的桥梁。本文将引导你探索RESTful API的设计哲学,通过实例展示如何构建一个高效、易于维护且具有扩展性的后端服务。从资源定义到HTTP方法的应用,再到状态码的精准使用,我们将一步步揭开高效后端API的秘密。
|
20天前
|
缓存 前端开发 API
打造高效后端API:RESTful设计原则与实践
【8月更文挑战第31天】 在数字化时代,后端API的设计至关重要。本文将深入探讨如何根据RESTful设计原则构建高效、易于维护和扩展的后端API。我们将从基础概念出发,逐步引导至高级实践,确保读者能够理解并应用这些原则,以提升API性能和开发效率。
|
20天前
|
UED 存储 自然语言处理
【语言无界·体验无疆】解锁Vaadin应用全球化秘籍:从代码到文化,让你的应用畅游世界每一个角落!
【8月更文挑战第31天】《国际化与本地化实战:构建多语言支持的Vaadin应用》详细介绍了如何使用Vaadin框架实现应用的国际化和本地化,提升用户体验和市场竞争力。文章涵盖资源文件的创建与管理、消息绑定与动态加载、日期和数字格式化及文化敏感性处理等方面,通过具体示例代码和最佳实践,帮助开发者构建适应不同语言和地区设置的Vaadin应用。通过这些步骤,您的应用将更加灵活,满足全球用户需求。
30 0
|
21天前
|
API 数据库 UED
全面解析构建高性能API的秘诀:运用Entity Framework Core与异步编程提升Web应用响应速度及并发处理能力的详细指南与实践案例
【8月更文挑战第31天】本文详细介绍了如何利用 Entity Framework Core (EF Core)的异步编程特性构建高性能 API。通过创建基于 EF Core 的 .NET Core Web API 项目,配置数据库上下文,并定义领域模型,文章展示了如何使用异步方法进行数据查询、加载相关实体及事务处理。具体代码示例涵盖了 GET、POST、PUT 和 DELETE 操作,全面展示了 EF Core 异步编程的优势,有助于提升 API 的响应速度和处理能力。
26 0
|
21天前
|
缓存 JavaScript API
构建高性能后端API:从理论到实践
【8月更文挑战第31天】 在数字化浪潮的推动下,后端API的性能成为影响用户体验和应用响应速度的关键因素。本文将带你深入理解高性能API的设计原则和实现方法,通过具体的代码示例展示如何优化API性能,确保你的后端服务能够快速、稳定地处理大量请求。