文件IO

Linux文件IO系统调用

Linux为用户提供了很多用于操作文件的系统调用api方便用户编写程序来操作文件。我们可以在linux中使用man手册来查看这些api的使用方法。

1. open

头文件

1
2
3
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

函数声明

1
2
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
  • open函数会打开pathname所对应的文件,如果文件不存在,则可以通过在flags中设置O_CREAT选项来创建文件。

  • open的返回值是文件的描述符(file descriptor, 一个用来标记文件的正整数),如果返回-1则表示打开/创建文件失败

  • 常用的flag有:O_RDONLY, O_WRONLY, 以及O_RDWR用来声明文件权限。

2. close

头文件

1
#include <unistd.h>

函数声明

1
int close(int fd);
  • 关闭文件标识符,当fd被关闭则无法再对文件标识符fd对应的文件进行操作。

3. read

头文件

1
#include <unistd.h>

函数声明

1
ssize_t read(int fd, void *buf, size_t count);
  • read函数尝试从fd标识的文件中读取count字节的数据到buf中
  • 当读取成功时,返回读取到数据的字节长度,文件指针的偏移改字节长度,通常用while循环读取文件。读取失败则返回-1.

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

char buf[2];//缓冲区,用来暂存读取的数据
int main(){
int fd = open("in.txt", O_RDONLY);
if(fd == -1){
perror("open");
}
int len = 0;
while((len = read(fd, (void *)buf, sizeof(buf)))> 0){
printf("%s", buf);
}
printf("\n");
close(fd);
}

4. write

头文件

1
#include <unistd.h>

函数声明

1
ssize_t write(int fd, const void *buf, size_t count);
  • write函数从buf中向fd标识的文件中写入最多count字节的数据
  • 写入成功则返回写入的字节数目

实例(实现复制文件功能)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
char buf[1024];
int main(){
int src_fd = open("src.txt", O_RDONLY);
int des_fd = open("des.txt", O_WRONLY | O_CREAT);
if(src_fd == -1 || des_fd == -1){
perror("open");
return -1;
}

int len = 0;
while((len = read(src_fd, (void *)buf, sizeof(buf))) > 0){
int ret = write(des_fd, (void *)buf, len);
if(ret == -1){
perror("write");
return -1;
}
}
close(des_fd);
close(src_fd);
}

5.lseek

头文件

1
2
#include <sys/types.h>
#include <unistd.h>

函数声明

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

whence选项:

  • SEEK_SET:将文件指针偏移量设置为offset
  • SEEK_CUR:文件指针偏移量设置为SEEK_CUR + offset
  • SEEK_END:文件偏移量设置为文件大小+offset

实例(忽略前32字节,复制部分内容)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
char buf[1024];
int main(){
int src_fd = open("src.txt", O_RDONLY);
int des_fd = open("des.txt", O_WRONLY | O_CREAT);
if(src_fd == -1 || des_fd == -1){
perror("open");
return -1;
}
int ret = lseek(src_fd, 32, SEEK_CUR);//移动指针
int len = 0;
while((len = read(src_fd, (void *)buf, sizeof(buf))) > 0){
int ret = write(des_fd, (void *)buf, len);
if(ret == -1){
perror("write");
return -1;
}
}
close(des_fd);
close(src_fd);
}

6. stat函数

头文件

1
2
3
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

函数声明

1
2
3
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
  • stat返回一个文件的信息,将信息保存到statbuf这个结构体中
  • lstat只返回符号链接的文件信息,而不返回所指向的文件信息
  • stat和fstat区别在于参数

文件信息结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
nlink_t st_nlink; /* Number of hard links */
uid_t st_uid; /* User ID of owner */
gid_t st_gid; /* Group ID of owner */
dev_t st_rdev; /* Device ID (if special file) */
off_t st_size; /* Total size, in bytes */
blksize_t st_blksize; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */

/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */

struct timespec st_atim; /* Time of last access */
struct timespec st_mtim; /* Time of last modification */
struct timespec st_ctim; /* Time of last status change */

#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};

7. rename

头文件

1
#include <stdio.h>

函数声明

1
int rename(const char *oldpath, const char *newpath);

实例

1
2
3
4
5
6
7
8
#include<stdio.h>

int main(){
int ret = rename("1.txt", "2.txt");
if(ret == -1){
perror("rename");
}
}

8. getcwd

头文件

1
#include <unistd.h>

函数定义

1
char *getcwd(char *buf, size_t size);
  • 将当前绝对路径传递给大小为size的buf

实例

1
2
3
4
5
6
7
#include<stdio.h>
#include <unistd.h>
int main(){
char buf[100];
char* ret = getcwd(buf, sizeof(buf));
printf("%s\n", ret);
}

9. mkdir&rmdir

头文件

1
2
3
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

函数声明

1
2
int mkdir(const char *pathname, mode_t mode);
int rmdir(const char *pathname);
  • 创建文件夹,名字为pathname,读写权限用八进制数字mode标识(rwxrwxrwx)
  • 删除文件夹

10. opendir

头文件

1
2
#include <sys/types.h>
#include <dirent.h>

函数声明

1
2
DIR *opendir(const char *name);
DIR *fdopendir(int fd);

实例(递归遍历获取文件个数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int getFileNum(const char * path);
int main(int argc, char *argv[]){
if(argc < 2){
printf("%s path\n", argv[0]);
return -1;
}
int num = getFileNum(argv[1]);
printf("普通文件的个数为:%d\n", num);
return 0;
}
int getFileNum(const char * path){
DIR *dir = opendir(path);
if(dir == NULL){
perror("opendir");
exit(0);
}
int tot = 0;
struct dirent *ptr;
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);
tot += getFileNum(newpath);
}
if(ptr->d_type == DT_REG){
tot++;
}
}
closedir(dir);
return tot;
}

11. dup

头文件

1
#include <unistd.h>

函数声明

1
2
int dup(int oldfd);
int dup2(int oldfd, int newfd);
  • 为一个文件复制一份新的文件描述符
  • dup2可以指定文件标识符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(){
int fd = open("in.txt", O_RDWR);
if(fd == -1){
perror("open");
}

int fd_new = dup(fd);
printf("%d %d\n", fd, fd_new);
char *str = "Hello world";
write(fd, str, strlen(str));
close(fd);
//指针归0
lseek(fd_new, 0, SEEK_SET);
int len = 0;
char buf[12];
while((len = read(fd_new, (void *)buf, sizeof(buf))) > 0){
printf("%s\n", buf);
}
}