posix(可移植操作系统接口)定义的一组函数,不提供缓冲机制,每次读写操作都引起系统调用,核心概念是文件描述符,访问各类型文件,在Linux下,标准I/O基于文件I/O实现。
文件描述符(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。
编写head.h的头文件
将head.h放到/usr/include路径下
修改head.h文件所属用户和组,sudo chown linux:linux /usr/include/head.h
修改用户代码片段(c.json),将#include
#ifndef __HEAD_H__
#define __HEAD_H__#include
#include
#include
#include
#include #define PRINT_ERR(msg) \do \{ \perror(msg); \return -1; \} while (0)#endif
#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置位错误码
文件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+” | 以读写和追加的方式打开文件,如果文件不存在就创建,如果文件存在读光标在开头,写光标在结尾 |
只读、只写方式打开文件。
#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;
}
如果文件不存在,创建文件,以只写方式打开文件。
如果文件存在,以只读方式打开文件。
#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;
}
关闭标准输出(fd = 1),终端不会输出。
#include int main(int argc, const char *argv[])
{puts("--------------start");close(1);puts("--------------end"); //关闭标准输出(fd = 1),终端不会输出这行return 0;
}
#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置位错误码
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;
}
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;
}
#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;
}
#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;
}
#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;
}
#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置位错误码
#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;
}
如果使用读写和追加的方式打开文件,即使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;
}
#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置位错误码
#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程序};
#include
#include struct group *getgrgid(gid_t gid);
功能:根据gid获取group结构体
参数:@gid:组号
返回值:成功返回结构体指针,失败返回NULL置位错误码struct group {char *gr_name; //组名char *gr_passwd; //组的密码gid_t gr_gid; //组号};
#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);
}