1.打开和关闭文件

1.1 open

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<unistd.h>
#include<fcntl.h>
int open(const char *pathname, int flags)
/*
pathname: 打开文件的路径名(相对、绝对)
flags: 打开文件的方式(O_RDONLY | O_WRONLY | O_RDWR) --- <fcntl.h>
return: 返回一个文件描述符或-1
*/
int open(const char* pathname, int flags, mode_t mode)
/*
pathname: 打开文件的相对路径
flags: 打开文件的方式(O_RDONLY|O_WRONLY|O_RDWR)
还有很多标签可以在man里查看
mode_t: 参数2如果指定了 O_CREAT, 则参数三传 8 进制数来描述文件权限
例如 0664表示 rw-rw-r--
*/

  注意,文件创建后的最终权限受到 open() 函数传入的参数影响,同时也受到 umask 屏蔽掩码共同影响

  最终创建出的文件权限 = mode & (~umask)

1.2 close

1
2
3
4
5
6
#include <unistd.h>
int close(int fd)
/*
fd: open的返回值(文件描述符)
return: 成功0, 失败-1, errno
*/

2.文件读取和写入

  read和write都是按字节操作的函数

2.1 read

1
2
3
4
5
6
7
8
9
10
11
12
13
ssize_t read(int fd, void *buf, size_t count)
/*
fd: 打开的文件描述符
buf: 读取数据到缓冲区
count: 缓冲区大小 size_t: 无符号整数 ssize_t 有符号整数

return:
成功: 读到文件末尾0,
未读到文件末尾>0
失败: -1
errno = EAGAIN(或EWOULDBLOCK): 非阻塞状态读(网络、设备)文件没读到数据
errno != EAGAIN(或EWOULDBLOCK): 错误
*/

2.2 write

1
2
3
4
5
6
7
8
9
ssize_t write(int fd, void *buf, size_t count)
/*
fd: 打开的文件描述符
buf: 储存写出数据的缓冲区地址
count: 缓冲区大小 size_t: 无符号整数 ssize_t 有符号整数
return:
成功: >0 写入的数据大小, =0 没有写入数据
失败: -1, errno
*/

2.3 与fgetc和fputc比较

  read 和 write 是系统调用,fgetc 和 fputc 是 c 语言提供的函数。下面两个函数举例,同样复制一个txt文件,每次复制一个字节,c 语言提供的函数速度会远远快于系统调用,因为 c 语言封装的 read 和write 有一个缓冲区,分别是预读入缓输出,虽然 fgetc 函数读取一个字节,但实际上它一次性会缓存4096字节在内存中,同样 fputc 虽然会写入一个字节,但它会先写入内存4096字节再一次性写入文件,这种机制避免了频繁IO降低性能

2.4 文件描述符

  PCB进程控制块里有一个成员是文件描述符表,open 函数返回的 fd 是一个下标,返回表中未被使用的最小的文件描述符(0、1、2分别是标准输入STDIN_FILENO、标准输出STDOUT_FILENO、标准出错STDERR_FILENO)

3. 错误代码处理

3.1 strerror

1
char* strerror(EINTR);       // 头文件<string.h>,引入errno.h头文件(错误号), 该函数会打印错误号对应的信息(EINTR其实是4)

3.2 perror

1
2
3
4
5
6
int fd = open(filepath, O_RDWR|O_CREAT|O_TRUNC, 0644);
if(fd == -1)
{
perror("open dst err"); // 打印该字符串以及errno错误号对应的错误信息
exit(1); // 程序非正常退出
}

4.改变已经打开文件的访问属性

4.1 fcntl (f control)

  这个函数功能非常多,这只是一个例子

1
2
3
4
5
6
7
8
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
// 获取文件权限
int flag = fcntl(fd, F_GETFL);
// 修改文件权限
flag |= O_NONBLOCK; // 例如添加非阻塞权限
// 设置文件权限
fcntl(fd, F_SETFL, flag);

5.改变文件指针

5.1 lseek

1
2
3
4
5
6
7
8
off_t lseek(int fd, off_t offset, int whence);
/*
fd: 文件描述符
offset: 偏移量
whence: 起始偏移位置 SEEK_SET起始位置 / SEEK_CUR当前位置 / SEEK_END结束位置

return: 成功返回起始位置的偏移, 失败返回-1
*/
  • 文件的“读”、“写”使用相同偏移位置,所以先使用 write 写入10个字节后,read 时指针也是在第10个字节开始的,这可能就需要 lseek 调整指针位置
  • 可以通过 int len = lseek(fd, 0, SEEK_END); 获取当前文件的大小,从结束位置偏移 0 个字节返回
  • 可以通过 int len = lseek(fd, 10, SEEK_END); 拓展文件大小,但必须引起IO操作才能拓展,write(fd, “a”, strlen(“a”)); 正常应该用 truncate() 函数扩展文件大小