文件操作
Linux中有一句非常经典的话—一切皆文件,Linux将一切硬件都通过文件的方式进行处理,所以对于文件的操作是一门必备学习。
每一个进程中会建立一个虚拟地址空间,对文件等进行控制,每个文件进程创建的文件占用一个文件描述符,内核空间中有一个PCB(进程控制块)其中有文件描述符,每个进程打开一个文件描述符会占用一个位置,每次占用的位置都是从最小的位置开始,前三个位置的状态被系统占用默认打开状态。
struct stat {
dev_t st_dev; /* ID of device containing file 文件的设备编号*/
ino_t st_ino; /* inode number 节点*/
mode_t st_mode; /* protection 文件的类型与存取权限*/
nlink_t nlink; /* number of hard links 链接到该文件的影链接数目*/
user_t st_uid; /* user ID of owner 用户ID*/
gid_t st_gid; /* group ID of owner 组ID*/
dev_t st_rdev; /* device ID (if special file) 设备文件的设备编号*/
off_t st_size; /* total size, in bytes 文件字节数(文件大小)*/
blksize_t st_blksize; /* blocksize for file system I/O 块大小*/
blkcnt_t st_blocks; /* number of 512B blocks allocated 块数*/
time_t st_atime; /* time of last access 最后一次访问时间*/
time_t st_mtime; /* time of last modification 最后一次修改时间*/
time_t st_ctime; /* time of last status change 组后一次改变时间(改变属性)*/
};
struct stat
是一个文件块中的信息,用函数 stat
可以获取路径下的文件信息
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
作用:查看文件的信息
-path:操作文件的路径
-buf: 结构体变量,传出参数,用于保存获取到的文件的信息
返回值:
成功:返回0
失败:返回-1, 设置errno
int lstat(const char *path, struct stat *buf);
作用:查看软连接文件的信息
-path:操作文件的路径
-buf: 结构体变量,传出参数,用于保存获取到的文件的信息
返回值:
成功:返回0
失败:返回-1, 设置errno
每一个文件都有自己的文件类型符st_mode,通过操作这个文件描述符可以改变文件的类型和权限等参数。
r-代表可读权限,w-代表可写权限,x代表可执行权限
前三位是其他组,其后三位是Group和Uer,通过设置各个组不同的权限可以使得文件被访问的状态。
- S_IFSOCK 0140000 套接字
- S_IFLNK 0120000 符号链接(软链接)
- S_IFREG 0100000 普通文件
- S_IFBLK 0060000 块设备
- S_IFDIR 0040000 目录
- S_IFCHR 0020000 字符设备
- S_IFIFO 0010000 管道
- S_IFMT 0170000 掩码
(st_mode & S_IFMT) == S_IFREG
setGID – 设置组id
setUID – 设置用户id
Sticky – 粘住位
ls-l模拟
//模拟实现ls -l功能
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <string.h>
#include <time.h>
int main (int argc, char* argv[]) {
if (argc < 2) {
printf("%s filename\n", argv[0]);
return -1;
}
//通过stat函数获取用户传入的文件信息
struct stat st;
int ret = stat (argv[1], &st);
if (ret == -1) {
perror("stat");
return -1;
}
//获取文件类型和文件权限
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_IFBLK:
perms[0] = 'b';
break;
case S_IFCHR:
perms[0] = 'c';
break;
case S_IFSOCK:
perms[0] = 's';
case S_IFIFO:
perms[0] = 'p';
default:
perms[0] = '?';
}
//判断文件的访问权限
//文件所有者
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;
//文件所在组
char* fileGrp = getgrgid(st.st_gid)->gr_name;
//文件大小
long int fileSize = st.st_size;
//获取修改时间
char* time = ctime(&st.st_mtime);
char mtime[512];
strncpy(mtime, time, strlen(time) - 1);
char buf[1024];
sprintf(buf, "%s %d %s %s %d %s %S", perms, linkNum, fileUser, fileGrp, fileSize, mtime,argv[1]);
printf("%s", buf);
return 0;
}
lseek
标准C库 lseek
#include <stdio.h>
int fseek(FILE \*stream, long offset, int whence);
linux lseek
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数:
- fd:文件描述符,通过open得到的,通过fd操作文件
- offset: 偏移量
- whence:
SEEK_SET
The offset is set to offset bytes.
设置文件指针的偏移量
SEEK_CUR
The offset is set to its current location plus offset bytes.
设置文件指针的偏移量:从当前位置 + 第二个参数offset的值
SEEK_END
The offset is set to the size of the file plus offset bytes.
设置偏移量:文件大小 + 第二个参数offset的值
返回值:返回文件指针的位置
作用:
1.移动文件指针到头文件
lseek(fd,0,SEEK_SET);
2.获取文件指针的位置
lseek(fd,0,SEEK_CUR);
3.获取文件长度
lseek (fd, 0, SEEK_END);
4.拓展文件的长度,当前文件10b->110b,增加100
lseek(fd, 100, SEEK_END);
注意:需要写一次数据,增加的长度才会生效
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
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;
}
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);
--必选项 O_RDONLY, O_WRONLY, or O_RDWR
–可选项 O_CREAT 文件不存在,创建新文件
mode :八进制的数,表示创建处的新的文件的操作权限
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int fd = open("create.txt", O_RDWR | O_CREAT,0777);
if (fd == -1) {
perror("open");
}
close(fd);
return 0;
}
read/write
#include <unistd.h>
ssize_t read(int fd, void \*buf, size_t count);
参数:
-fd :文件描述符,通过文件描述符操作文件
-buf:需要读取存放的地方,数组的地址(传出参数)
-count: 指定的数组的大小
返回值:ssize_t
-成功:
>0: 返回实际读取到的字节数
==0: 文件已经读取完了
-失败:
-1, 并且设置errno
#include <unistd.h>
ssize_t write(int fd, const void \*buf, size_t count);
参数:
- fd:文件描述符,通过文件描述符操作文件
-buf: 要往磁盘写入的数据
-count:要写的数据的实际大小
返回值:
成功:实际写入的字节数
失败:返回-1,并设置errno
int main() {
// 通过open打开文件
int srcfd = open("file.txt",O_RDONLY);
if (srcfd == - 1) {
perror("open");
return -1;
}
//创建一个新的文件(拷贝文件)
int destfd = open("copy.txt", O_WRONLY | O_CREAT, 0664);
if (destfd == -1) {
perror("open");
return -1;
}
//频繁的读写操作
char buf[1023] = {0};
int len = 0;
while(len = read(srcfd, buf, sizeof(buf)) > 0) {
write(destfd, buf, len);
}
//关闭文件
close(destfd);
close(srcfd);
return 0;
}
access
#include <unistd.h>
int access(const char *pathname, int mode);
作用:判断某个文件是否有某个权限,或者判断文件是否存在
参数:
-pathname:判断文件的路径
-mode:
R_OK:判断是否有读权限
W_OK:判断是否有写权限
X_OK:判断是否有执行权限
F_OK:判断分件是否存在
返回值: 成功返回0,失败返回-1
#include <unistd.h>
#include <stdio.h>
int main () {
int ret = access("hello.txt",F_OK);
if (ret == -1) {
perror("access");
return -1;
}
printf("文件存在!!");
return 0;
}
chmod
#include <sys/stat.h>
int chmod(const char \*path, mode_t mode);
作用:修改文件的权限
参数:
-pathname:需要修改的文件的路径
-mode:需要修改的权限值,八进制的值
#include<sys/stat.h>
#include<stdio.h>
int main () {
int ret = chmod("hello.txt", 0775);
if (ret == -1) {
perror("chmod");
return -1;
}
return 0;
}
truncate
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
作用:缩减或者扩展文件的尺寸到指定的大小
参数:
-path:需要修改的文件的路径
-length: 需要最终文件变成的大小
#include <unistd.h>
#include <sys/types.h>
int main () {
int ret = truncate("hello.txt",20);
if (ret == -1) {
perror("truncate");
return -1;
}
return 0;
}
rename
#include <stdio.h>
int rename(const char \*oldpath, const char \*newpath);
作用:改变文件名称
#include <stdio.h>
int main () {
int ret = rename("hello.txt","abc");
if (ret == -1) {
perror("rename");
return -1;
}
}
chdir/getcwd
#include <unistd.h>
int chdir(const char *path);
作用:修改当前进程的目录(在/code下启动了一个可执行程序a.o,进程的工作的路径就是/code)
参数:
path:需要修改到的目的工作目录
#include <unistd.h>
char* getcwd(char *buf, size_t size);
作用:获取当前工作目录
参数:
buf:存储的路径,指向的是一个数组----也是传出的参数
size:数组的大小
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/type.h>
#include <fcntl.h>
int main () {
//获取当前的工作目录
char buf[128];
getcwd(buf, sizeof(buf));
printf("当前工作的目录是:%s\n", buf);
//修改工作目录
int ret = chdir("...");
if (ret == -1) {
perror("chdir");
return -1;
}
}
mkdir
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char \*pathname, mode_t mode);
作用:创建一个目录
参数:
pathname:创建目录的路径
mode:权限,八进制的数
返回值:
成功返会0,失败返回-1
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
int main () {
int ret = mkdir("abc", 0777);
if (ret == -1) {
perror("mkdir");
return -1;
}
return 0;
}
opendir/closedir/readdir
//打开一个目录
#include <sys/types.h>
#include <dirent.h>
qDIR *opendir(const char *name);
参数:
name:需要打开的目录名称
返回值:
Dir* 类型,理解为目录流
//读取目录中的内容
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
参数:dirp是opendir返回的结果
返回值:
struct dirent
,返回读到的文件信息
读取到末尾或者失败,会返回NULL
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR \*dirp);
struct dirent {
ino_t d_ino; /* inode number 此目录进入点的inode*/
off_t d_off; /* not an offset; see NOTES
目录文件开头至此目录进入点的位移*/
unsigned short d_reclen; /* length of this record
d_name 的长度, 不包含NULL字符*/
unsigned char d_type; /* type of file; not supported by all file system types
d_name 所指的文件类型*/
char d_name[256]; /* filename 文件名*/
};
//读取某个目录下所有的普通文件的个数
int main (int argc, char* argv[]) {
if (argc < 2) {
printf("%s path\n", argv[0]);
return -1;
}
int num = gerFileNum(argv[1]);
printf("普通文件个数为:%d",num);
return 0;
}
int gerFileNum(const char* path) {
//打开目录
DIR* dir = opendir(path);
if (dir == NULL) {
perror("opendir");
exit(0);
}
struct dirent* ptr;
int total = 0;
while((ptr = readdir(dir)) != NULL) {
//获取名称
char* dname = ptr->d_name;
//忽略掉.和..
if (strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) {
continue;
}
//判断是普通文件还是目录
if (ptr->d_type == DT_DIR) {
char newpath[256];
sprintf(newpath, "%s/%s", path, dname);
total += gerFileNum(newpath);
}
if (ptr->d_type == DT_REG) {
//普通文件
total++;
}
}
//关闭目录
closedir(dir);
return total;
}
dup/dup2
#include <unistd.h>
int dup(int oldfd);
作用:复制一个新的文件描述符
fd = 3, int fd1 = dup(fd)
fd指向的是a.txt,fd1也是指向a.txt
从空闲的(文件描述符表)中的位置找到一个最小的作为文件描述符
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main () {
int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
if (fd == -1) {
perror("open");
return -1;
}
int fd1 = dup(fd);
if (fd1 == -1) {
perror("dup");
return -1;
}
printf("fd: %d, fd1: %d\n", fd, fd1);
close(fd);
char* tmp = "hello, world";
int ret = write(fd1, tmp, sizeof(tmp));
if (ret == -1) {
perror("write");
return -1;
}
close(fd1);
return 0;
}
#include <unistd.h>
int dup2(int oldfd, int newfd);
作用:重定向文件描述符
oldfd指向a.txt , newfd指向b.txt
调用函数成功后:newfd和b.txt做close,newfd指向了a.txt
oldfd必须是一个有效的文件描述符
oldfd和newfd值相同,相当于什么都没有做
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
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);
if (fd1 == -1) {
perror("open");
return -1;
}
printf("fd: %d fd1: %d\n", fd, fd1);
int fd2 = dup2(fd, fd1);
if (fd2 == -1) {
perror("dup2");
return -1;
}
char* tmp = "hello, dup2";
int len = write(fd1, tmp, strlen(tmp));
if (len == -1) {
perror("write");
return -1;
}
printf("fd: %d, fd1: %d, fd2: %d\n",fd,fd1,fd2);
close(fd);
close(fd1);
return 0;
}
fcntl
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /arg/ );
参数:
fd: 表示需要操作的文件描述符
cmd: 表示对文件描述符进行如何操作
- F_DUPFD:复制文件描述符,复制的是fd
- F_GETFL:获取指定的文件描述符状态flag
flag就是open函数转递的flag如O_CREAT等
-F_SETFL:设置文件描述符文件状态flag
必选项:O_RDONLY,O_WRONLY,O_RDWR不可被修改
可选项:O_APPEND,O_NONBLOCK
O_APPEND表示追加数据
O_NONBLOCK 设置成非阻塞
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main () {
//1.复制文件描述符
//int fd = open("1.txt",O_RDONLY);
//int ret = fcntl(fd, F_DUPFD);
//2.修改或者获取文件状态flag
int fd = open("1.txt", O_RDWR);
if (fd == -1) {
perror("open");
return -1;
}
//修改文件描述符状态的flag,给flag加入O_APPEND这个标记
int flag = fcntl(fd, F_GETFL);
flag |= O_APPEND;
int ret = fcntl(fd, F_SETFL, flag);
char* str = " world";
write(fd, str, strlen(str));
close(fd);
return 0;
}