open
打开文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
// 打开一个已经存在的文件
- pathname: 文件路径
- flags: 文件的权限设置
- O_RDONLY, O_WRONLY, O_RDWR
// 返回值:成功返回文件描述符,失败返回-1,设置errno
============================================
#include <unistd.h>
int close(int fd);
// 关闭指定的文件描述符
============================================
#include <stdio.h>
void perror(const char *s);
// 打印errno对应的错误描述
打开a.txt文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main() {
int fd = open("a.txt", O_RDONLY);
if (fd == -1) {
perror("open");
}
close(fd); // 关闭文件
return 0;
}
创建文件
int open(const char *pathname, int flags, mode_t mode);
// 可以创建文件
- pathname: 文件路径
- flags: 文件的权限设置,必选,互斥
- O_RDONLY, O_WRONLY, or O_RDWR (必选,互斥)
- O_CREAT
- mode: 设置文件访问权限的初始值,八进制数,如0775,最终的权限是 mode&~umask
umask: 002, ~umask=0775
第三个参数是在第二个参数中有O_CREAT时才作用,如果没有,则第三个参数可以忽略
- 文件权限。r:可读,w:可写,x:可执行
- 三个一组,第一组为当前用户,第二组为用户所在组,第三组是其他组
创建一个create.txt
文件
int main() {
int fd = open("create.txt", O_RDWR | O_CREAT, 0777);
if (fd == -1) {
perror("open");
}
close(fd);
return 0;
}
read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
- fd: 文件描述符,通过open得到
- buf: 读取文件存放的地方,数组的地址,传出参数
- count: 数组大小
// 返回值:成功返回读取到的字节数,0表示到了文件末尾,-1表示出错
write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
- fd: 文件描述符
- buf: 要写入的数据
- count: 要写的数据的大小
// 成功返回写入的字节数,0表示没有内容写了,失败返回-1
用read和write实现文件拷贝
int main() {
int srcfd = open("e.txt", O_RDONLY);
if (srcfd == -1) {
perror("open");
return -1;
}
int destfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
if (destfd == -1) {
perror("open");
return -1;
}
char buf[1024] = {0};
int len = 0;
while ((len = read(srcfd, buf, sizeof(buf))) > 0) {
printf("read %d bytes\n", len);
write(destfd, buf, len);
}
close(srcfd);
close(destfd);
return 0;
}
lseek
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
- fd: 文件描述符
- offset: 偏移量
- whence
- SEEK_SET: 设置文件指针偏移量
- SEEK_CUR: 从当前位置+offset偏移
- SEEK_END: 文件结尾+offset
// 返回偏移后的文件指针位置
1. 移动文件指针到文件头
lseek(fd, 0, SEEK_SET);
2. 获取当前文件指针位置
lseek(fd, 0, SEEK_CUR);
3. 获取文件长度
lseek(fd, 0, SEEK_END);
4. 拓展文件长度
lseek(fd, 100, SEEK_END); // 从文件末尾拓展100字节
C库里面的类似
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
- stream: FILE文件指针
拓展hello.txt文件
int main() {
int fd = open("hello.txt", O_RDWR);
if (fd == -1) {
perror("open");
return -1;
}
int ret = lseek(fd, 100, SEEK_END);
if (ret == -1) {
perror("lseek");
return -1;
}
write(fd, " ", 1); // 最后写入一个空格
close(fd);
return 0;
}
stat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
// 获取文件信息
- pathname: 文件路径
- statbuf: 保存文件信息,传出参数
// 成功返回0,失败返回-1,设置错误号
struct stat {
dev_t st_dev; /* 块设备号(ID) */
ino_t st_ino; /* inode结点号,文件属性信息所存inode节点的编号 */
mode_t st_mode; /* 文件类型和文件权限*/
nlink_t st_nlink; /* 链接数 */
uid_t st_uid; /* 文件所属用户ID*/
gid_t st_gid; /* 文件所属组ID */
dev_t st_rdev; /* 字符设备ID */
off_t st_size; /* 文件大小 */
blksize_t st_blksize; /* 系统每次按块Io操作时,块的大小(一般是512或1024) */
blkcnt_t st_blocks; /* 块的索引号 */
time_t st_atime; /* 最后一次访问时间,read*/
time_t st_mtime; /* 最后一次修改时间,write */
time_t st_ctime; /* 最后一次属性修改的时间,如权限被修改,文件所有者(属主)被修改 */
};
文件类型信息包含在结构体st_mode
成员中,以下这些宏的参数都是st_mode
,也就是说通过st_mode
成员可以判断文件类型,或者通过st_mode&S_IFMT
的结果判断
S_ISREG(m) 是否为普通文件
S_ISDIR(m) 是否为目录
S_ISCHR(m) 是否为字符设备
S_ISBLK(m) 是否为块设备
S_ISFIFO(m) 是否为FIFO(命名管道文件,用于进程通信)
S_ISLNK(m) 是否为符号链接
S_ISSOCK(m) 是否为套接字
st_mode
的0-8位保存文件的访问权限
st_mode | 权限 |
---|---|
S_IRUSR | 用户读 |
S_IWUSR | 用户写 |
S_IXUSR | 用户执行 |
S_IRGRP | 组读 |
S_IWGRP | 组写 |
S_IXGRP | 组执行 |
S_IROTH | 其他读 |
S_IWOTH | 其他写 |
S_IXOTH | 其他执行 |
int lstat(const char *pathname, struct stat *statbuf);
// 获取软连接本身的文件信息
ln -s a.txt b.txt // 创建一个软连接b.txt
// lrwxrwxrwx b.txt -> a.txt
用stat
查看文件大小
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main() {
struct stat statbuf;
int ret = lstat("b.txt", &statbuf);
printf("size: %ld\n", statbuf.st_size);
return 0;
}
用stat
实现ls -a
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("%s filename\n", argv[0]);
}
struct stat st;
int ret = stat(argv[1], &st);
char perms[11] = {0};
switch (st.st_mode & S_IFMT)// 文件类型
{
case S_IFLNK:
perms[0] = 'l';
break;
case S_IFDIR:
perms[0] = 'd'; // 目录
break;
case S_IFREG:
perms[0] = '-'; // 普通文件
break;
case S_IFBLK:
perms[0] = 'b';
break;
case S_IFCHR:
perms[0] = 'c';
break;
case S_IFSOCK:
perms[0] = 's';
break;
case S_IFIFO:
perms[0] = 'p';
break;
default:
perms[0] = '?';
break;
}
// 文件权限
perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';
perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-';
perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';
perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';
perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-';
perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-';
perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';
int linkNum = st.st_nlink; // 链接数量
char *fileuser = getpwuid(st.st_uid)->pw_name; // 用户id
char *filegrp = getgrgid(st.st_gid)->gr_name; // 组id
long fileSize = st.st_size; // 文件大小
char *time = ctime(&st.st_mtim.tv_sec);
char mtime[512] = {0};
strncpy(mtime, time, strlen(time) - 1);
char buf[1024];
sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileuser, filegrp, fileSize, mtime, argv[1]);
printf("%s\n", buf);
return 0;
}
dup
拷贝文件描述符
#include <unistd.h>
int dup(int oldfd);
- oldfd: 要拷贝的文件描述符
- 返回值: 新的文件描述符
int main() {
int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
int fd1 = dup(fd); // 拷贝文件描述符
if (fd1 == -1) {
perror("open");
return -1;
}
printf("fd: %d, fd1: %d\n", fd, fd1);
close(fd);
char *str = "hello,world";
int ret = write(fd1, str, strlen(str));
if (ret == -1) {
perror("write");
return -1;
}
close(fd1);
return 0;
}
dup2
重定向文件描述符
int dup2(int oldfd, int newfd);
newfd
关闭原来的文件,然后指向oldfd
的文件,oldfd
要有效
操作newfd
就相当于操作oldfd
int main() {
int fd = open("1.txt", O_RDWR | O_CREAT, 0664);
if (fd == -1) {
perror("open");
return -1;
}
int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);
printf("fd: %d, fd1: %d\n", fd, fd1); // fd: 3, fd1: 4
int fd2 = dup2(fd, fd1);
if (fd2 == -1) {
perror("dup2");
return -1;
}
// 现在fd和fd1都指向1.txt
char *str = "hello, world";
int ret = write(fd1, str, strlen(str));
if (ret == -1) {
perror("write");
return -1;
}
printf("fd: %d, fd1: %d, fd2: %d\n", fd, fd1, fd2); // fd: 3, fd1: 4, fd2: 4
close(fd);
close(fd1);
return 0;
}
fcntl
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... );
- fd: 表示需要操作的文件描述符
- cmd: 表示对文件描述符进行何种操作
- F_DUPFD: 复制文件描述符,复制fd,结果返回
- F_GETFL: 获取指定文件描述符文件状态flag
flag和通过open函数传递的flag是一个东西
- F_SETFL: 设置文件描述符文件状态flag
必选项: O_RDONLY, O_WRONLY, O_RDWR
可选项: O_APPEND, O_NONBLOCK(设置成非阻塞)
int ret = fcntl(fd, F_DUPFD); // 复制fd
int main() {
int fd = open("1.txt", O_RDWR); // 打开文件
int flag = fcntl(fd, F_GETFD); // 获取原先的文件描述符
flag |= O_APPEND; // 添加追加flag
int ret = fcntl(fd, F_SETFL, flag); // 设置为新的flag
char *str = "hello";
write(fd, str, strlen(str));
close(fd);
return 0;
}