【文件I/O】(二)文件I/O
创始人
2024-03-15 10:13:50
0

文件I/O:系统调用

  • 一、文件I/O基本概念
    • 1.什么是文件I/O?
    • 2.文件描述符
  • 二、文件I/O函数
    • head.h
    • 1.open、close(打开、关闭文件)
      • 1.1open、close函数API
      • 1.2文件I/O和标准I/O文件打开方式对比
      • 1.3open函数实例1
      • 1.4open函数实例2
      • 1.5close函数实例3
    • 2.read、write(读取、写入文件)
      • 2.1read、write函数API
      • 2.2read函数实例1
      • 2.3write函数实例2
      • 2.4write函数实例3(计算文件大小)
      • 2.5read、write函数实例4(文件拷贝)
      • 2.6fgets、write函数实例5(键盘输入内容到文件)
    • 3.lseek(文件指针偏移)
      • 3.1lseek函数API
      • 3.2lseek函数实例1
      • 3.3lseek函数实例2
    • 4.stat(获取文件属性)
      • 4.1stat函数API
      • 4.2根据uid获取用户名,getpwuid函数
      • 4.3根据gid获取组名,getgrgid函数
      • 4.4stat函数实例1(实现 ls -l)

一、文件I/O基本概念

1.什么是文件I/O?

posix(可移植操作系统接口)定义的一组函数,不提供缓冲机制,每次读写操作都引起系统调用,核心概念是文件描述符,访问各类型文件,在Linux下,标准I/O基于文件I/O实现。

2.文件描述符

文件描述符(fd)是一个非负整数,Linux为程序中每个打开的文件分配一个文件描述符。文件描述符从0开始分配,依次递增,在ubuntu系统中一个正在执行的程序,文件描述符的范围是 0~1023(1024个)。每一个正在执行的程序都有自己的一套文件描述符,相互不干扰。

一个正在执行的程序中0,1,2文件描述符已经被占用了,分别对应的是标准输入,标准输出,标准出错。

#include int main(int argc, const char* argv[])
{printf("in  fd = %d\n", stdin->_fileno);  // 0printf("out fd = %d\n", stdout->_fileno); // 1printf("err fd = %d\n", stderr->_fileno); // 2return 0;
}

ulimit -a 显示当前所有的资源限制等。
ulimit -n 2048 修改文件描述符范围为2048。

二、文件I/O函数

head.h

编写head.h的头文件
将head.h放到/usr/include路径下
修改head.h文件所属用户和组,sudo chown linux:linux /usr/include/head.h
修改用户代码片段(c.json),将#include 改成#include

#ifndef __HEAD_H__
#define __HEAD_H__#include 
#include 
#include 
#include 
#include #define PRINT_ERR(msg) \do                 \{                  \perror(msg);   \return -1;     \} while (0)#endif

1.open、close(打开、关闭文件)

1.1open、close函数API

#include 
#include 
#include int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
功能:打开文件
参数:@pathname:打开文件的路径及名字@flags   :打开文件的方式O_RDONLY :只读O_WRONLY :只写O_RDWR   :读写O_APPEND :追加O_CREAT  :创建,如果flags中填写了O_CREAT,就必须填写第三个参数,第三个参数代表创建文件的权限O_TRUNC  :清空O_EXCL   :和O_CREAT结合使用,在创建文件的时候加上这个选项,如果文件存在就返回EEXIST,如果文件不存在就会不返回错误O_NOCTTY :使用本参数时,如文件为终端,那么终端不可作为调用open()系统调用的那个进程的控制终端。@mode:创建文件的权限,为8进制表示法实际创建出来的文件的权限 = (mode & ~umask)umask:文件的掩码:通过umask命令可以查看掩码的默认值是0002  ~umask:文件掩码的取反是在最大默认文件的权限的基础上取反的,最大默认文件的权限是0666实际创建出来的文件的权限 = (0666 & ~(0002)) =  (0666 & 0664) = 0664
返回值:成功返回文件描述符,失败返回-1置位错误码
#include int close(int fd);
功能:关闭文件
参数:@fd:文件描述符
返回值:成功返回0,失败返回-1置位错误码

1.2文件I/O和标准I/O文件打开方式对比

文件IO标准IO功能
O_RDONLY“r”以只读的方式打开文件,将光标定位到开头
O_RDWR“r+”以读写的方式打开文件,将光标定位到开头
O_WRONLY | O_CREAT | O_TRUNC,0666“w”以只写的方式打开文件,如果文件不存在就创建,如果文件存在清空,光标在开头
O_RDWR | O_CREAT | O_TRUNC,0666“w+”以读写的方式打开文件,如果文件不存在就创建,如果文件存在清空,光标在开头
O_WRONLY | O_CREAT | O_APPEND,0666“a”以只写和追加的方式打开文件,如果文件不存在就创建,如果存在光标在结尾
O_RDWR | O_CREAT | O_APPEND,0666“a+”以读写和追加的方式打开文件,如果文件不存在就创建,如果文件存在读光标在开头,写光标在结尾

1.3open函数实例1

只读、只写方式打开文件。

#include 
#include 
#include 
#include 
#include #define PRINT_ERR(msg) \do                 \{                  \perror(msg);   \return -1;     \} while (0)int main(int argc, const char *argv[])
{int fd1, fd2;// 只读方式打开文件if (-1 == (fd1 = open("./1.txt", O_RDONLY)))PRINT_ERR("open error");// 只写方式打开文件if (-1 == (fd2 = open("./hello.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666)))PRINT_ERR("open error");close(fd1);close(fd2);return 0;
}

1.4open函数实例2

如果文件不存在,创建文件,以只写方式打开文件。
如果文件存在,以只读方式打开文件。

#include int main(int argc, const char *argv[])
{int fd;// 如果文件不存在,创建文件,以只写方式打开文件// 如果文件存在,以只读方式打开文件if (-1 == (fd = open("./1.txt", O_WRONLY | O_CREAT | O_EXCL, 0666))) {if (errno = EEXIST) {if (-1 == (fd = open("./1.txt", O_RDONLY)))PRINT_ERR("open error");puts("文件存在,以只读方式打开文件");} else {PRINT_ERR("open error");}} else {puts("文件不存在,创建文件,以只写方式打开文件");}printf("fd = %d\n", fd);close(fd);return 0;
}

1.5close函数实例3

关闭标准输出(fd = 1),终端不会输出。

#include int main(int argc, const char *argv[])
{puts("--------------start");close(1);puts("--------------end");  //关闭标准输出(fd = 1),终端不会输出这行return 0;
}

2.read、write(读取、写入文件)

2.1read、write函数API

#include ssize_t read(int fd, void *buf, size_t count);
功能:读取文件中的内容
参数:@fd:文件描述符@buf:存储读取到数据的首地址@count:想要读取的字节的个数
返回值:成功返回读取到字节的个数,如果返回0代表读取到文件的结尾了失败返回-1置位错误码
#include ssize_t write(int fd, const void *buf, size_t count);
功能:向文件中写数据
参数:@fd:文件描述符@buf:想要写的数据的首地址@count:想要写的字节的个数
返回值:成功返回写入的字节的个数,0表示不会写任何的数据失败返回-1置位错误码

2.2read函数实例1

1.验证读取整数
2.验证读取字符串
3.验证读取结构体
配合2.3wirte函数实例2使用

#include typedef struct stu {char name[20];int age;char sex;
}stu_t;int main(int argc, const char *argv[])
{int fd;if (-1 == (fd = open("./1.txt", O_RDONLY)))PRINT_ERR("open error");/*    // 1.验证读取整数int num;read(fd, &num, sizeof(num));printf("num = %d\n", num);// 2.验证读取字符串char buf[20] = {0};read(fd, buf, sizeof(buf));printf("buf = %s\n", buf);*/// 3.验证读取结构体stu_t stu;read(fd, &stu, sizeof(stu));printf("name = %s, age = %d, sex = %c\n", stu.name, stu.age, stu.sex);close(fd);return 0;
}

2.3write函数实例2

1.验证写入整数
2.验证写入字符串
3.验证写入结构体
配合2.2read函数实例1使用

#include typedef struct stu {char name[20];int age;char sex;
}stu_t;int main(int argc, const char *argv[])
{int fd;if (-1 == (fd = (open("./1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666))))PRINT_ERR("open error");
/* // 1.验证写入整数int num = 12345;write(fd, &num, sizeof(num));// 2.验证写入字符串char buf[100] = "hello world!";write(fd, buf, strlen(buf));*/// 3.验证写入结构体stu_t stu = {.name = "xiaoming",.age = 21,.sex = 'm'};write(fd, &stu, sizeof(stu_t));close(fd);return 0;
}

2.4write函数实例3(计算文件大小)

#include int main(int argc, const char *argv[])
{if (2 != argc) {fprintf(stderr, "Usage: %s \n", argv[0]);return -1;}int fd;if (-1 == (fd = open(argv[1], O_RDONLY)))PRINT_ERR("open error");int total = 0;int ret;char buf[10] = {0};while (0 < (ret = read(fd, buf, sizeof(buf)))) {total += ret;}printf("%s size total is %d\n", argv[1], total);close(fd);return 0;
}

2.5read、write函数实例4(文件拷贝)

#include int main(int argc, const char *argv[])
{// 1.校验用户输入的参数是否正确if (3 != argc) {fprintf(stderr, "Usage: %s  \n", argv[0]);return -1;}// 2.只读方式打开源文件,写的方式打开目标文件(创建)int sfd, dfd;if (-1 == (sfd = open(argv[1], O_RDONLY)))PRINT_ERR("open src_file error");if (-1 == (dfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)))PRINT_ERR("open dest_file error");// 3.拷贝int ret;char buf[10] = {0};while (0 < (ret = read(sfd, buf, sizeof(buf)))) {write(dfd, buf, ret);}// 4.关闭文件close(sfd);close(dfd);return 0;
}

2.6fgets、write函数实例5(键盘输入内容到文件)

#include int main(int argc, const char *argv[])
{if (2 != argc) {fprintf(stderr, "Usage: %s \n", argv[0]);return -1;}int fd;if (-1 == (fd = open(argv[1], O_RDWR | O_APPEND | O_CREAT, 0666)))PRINT_ERR("open error");char buf[10] = {0};while (NULL != fgets(buf, 10, stdin)) {if (0 == strcmp(buf, "quit\n")) break;  //终端输入quit退出write(fd, buf, strlen(buf));}close(fd);return 0;
}

3.lseek(文件指针偏移)

3.1lseek函数API

#include 
#include off_t lseek(int fd, off_t offset, int whence);
功能:设置光标的位置
参数:@fd:文件描述符@offset:偏移量>0:向后偏移=0:不偏移<0:向前偏移@whence:从那个位置开始偏移SEEK_SET:从文件的开头SEEK_CUR:从当前位置SEEK_END:从文件的结尾
返回值:成功返回光标到开头的字节数,失败返回-1置位错误码

3.2lseek函数实例1

#include int main(int argc, const char *argv[])
{// abcdefg1234567// 1.读写方式打开文件int fd;if (-1 == (fd = open("./1.txt", O_RDWR)))PRINT_ERR("open error");// 2.文件开头向后偏移5个字节lseek(fd, 5, SEEK_SET);char ch;read(fd, &ch, 1);printf("ch = %c\n", ch);  //读取到第6个字符,fch = 'Q';write(fd, &ch, 1);  //写入到第7个字符,abcdefQ1234567close(fd);return 0;
}

3.3lseek函数实例2

如果使用读写和追加的方式打开文件,即使lseek偏移了文件指针,写指针永远是在结尾。

#include int main(int argc, const char *argv[])
{// abcdefg1234567// 1.以读写和追加的方式打开文件int fd;if (-1 == (fd = open("./1.txt", O_RDWR | O_APPEND | O_CREAT, 0666)))PRINT_ERR("open error");// 2.文件开头向后偏移5个字节lseek(fd, 5, SEEK_SET);char ch;read(fd, &ch, 1);printf("ch = %c\n", ch);  //读取到第6个字符,fch = 'T';write(fd, &ch, 1);  //T字符写到了文件的结尾,写永远是在结尾int ret;ch = 0;ret = read(fd, &ch, 1);  //光标已经在结尾了,这里读是读取不到内容的,read返回0(end of file)printf("ch = %c, ret = %d\n", ch, ret);  //ch = , ret = 0close(fd); return 0;
}

4.stat(获取文件属性)

4.1stat函数API

#include 
#include 
#include int stat(const char *pathname, struct stat *statbuf);
//stat不能获取软连接文件,如果向测试软链接,使用lstat完成,用法和stat完全相同
功能:获取文件的属性信息
参数:@pathname:文件的路径及名字@statbuf:获取的属性信息结构指针struct stat {dev_t     st_dev;         //磁盘设备号,底层驱动课程讲解ino_t     st_ino;         //文件的inode号,文件系统识别文件的唯一编号,通过ls -i查看mode_t    st_mode;        //文件的类型和权限//1.文件的类型 文件的类型占4bit位,12-15就是文件的类型S_IFMT     0170000   bit mask for the file type bit fieldS_IFSOCK   0140000   socket           //套接字文件S_IFLNK    0120000   symbolic link    //软连接文件S_IFREG    0100000   regular file     //普通文件S_IFBLK    0060000   block device     //块设备文件S_IFDIR    0040000   directory        //目录S_IFCHR    0020000   character device //字符设备文件S_IFIFO    0010000   FIFO             //管道eg:      if((st_mode & S_IFMT) == S_IFREG){printf("普通文件\n");} //2.文件的权限 文件的权限占9bit位,0-8就是文件的权限 文件的权限 = st_mode & 0777nlink_t   st_nlink;       //硬链接数uid_t     st_uid;         //用户的uidgid_t     st_gid;         //用户的giddev_t     st_rdev;        //设备号,底层驱动课程讲解off_t     st_size;        //文件的大小,单位是字节blksize_t st_blksize;     //操作块设备的最小单位block,512字节blkcnt_t  st_blocks;      //文件的大小,单位是blockstruct timespec st_atim;  //最后一次访问这个文件的时间struct timespec st_mtim;  //最后一次修改这个文件的时间struct timespec st_ctim;  //最后一次状态改变的时间};
返回值:成功返回0,失败返回-1置位错误码

4.2根据uid获取用户名,getpwuid函数

#include 
#include struct passwd *getpwuid(uid_t uid);
功能:根据uid获取用户信息结构体
参数:@uid:用户的id
返回值:成功返回用户信息结构指针,失败返回NULL,置位错误码struct passwd {char   *pw_name;       //用户名char   *pw_passwd;     //用户的密码uid_t   pw_uid;        //uidgid_t   pw_gid;        //gidchar   *pw_gecos;      //用户的信息char   *pw_dir;        //用户的主目录char   *pw_shell;      //shell程序};

4.3根据gid获取组名,getgrgid函数

#include 
#include struct group *getgrgid(gid_t gid);
功能:根据gid获取group结构体
参数:@gid:组号
返回值:成功返回结构体指针,失败返回NULL置位错误码struct group {char   *gr_name;        //组名char   *gr_passwd;      //组的密码gid_t   gr_gid;         //组号};

4.4stat函数实例1(实现 ls -l)

#include void print_type(mode_t mode);
void print_permission(mode_t mode);
int print_time(struct timespec time);int main(int argc, const char *argv[])
{// 1.校验参数输入是否正确if (2 != argc) {fprintf(stderr, "Usage: %s \n", argv[0]);return -1;}// 2.获取文件的属性struct stat st;if (stat(argv[1], &st))PRINT_ERR("stat get msg failed");// 3.将属性打印出来print_type(st.st_mode);  //打印文件类型print_permission(st.st_mode);  //打印文件权限printf(" %ld", st.st_nlink);  //打印硬链接个数printf(" %s", getpwuid(st.st_uid)->pw_name);  //打印用户名printf(" %s", getgrgid(st.st_gid)->gr_name);  //打印组名printf(" %ld", st.st_size);  //打印文件大小(字节)print_time(st.st_atim);  //打印时间戳printf(" %s", argv[1]);  //打印文件名puts("");return 0;
}// 打印文件类型
void print_type(mode_t mode) {switch (mode & S_IFMT) {case S_IFSOCK:putchar('s'); break;case S_IFLNK:putchar('l'); break;case S_IFREG:putchar('-'); break;case S_IFBLK:putchar('b'); break;case S_IFDIR:putchar('d'); break;case S_IFCHR:putchar('c'); break;case S_IFIFO:putchar('p'); break;}
}// 打印文件权限
void print_permission(mode_t mode) {for (int n = 8; n >= 0; n--) {if ((mode & 0777) & (1 << n)) {switch (n % 3) {case 2:putchar('r'); break;case 1:putchar('w'); break;case 0:putchar('x'); break;}} else {putchar('-');}}
}// 打印文件时间戳
int print_time(struct timespec time) {struct tm* tm;if (NULL == (tm = localtime(&time.tv_sec)))PRINT_ERR("change time error");printf(" %02d月 %2d %02d:%02d", \tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
}

相关内容

热门资讯

汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...