1.相关概念
文件io就是系统调用,不涉及缓冲区,只要执行一次就会调用linux内核的系统调用,然后对硬件设备进行空间
标准IO对文件操作使用文件指针
文件IO对文件操作使用文件描述符
文件描述符:
顺序分配的非负整数
内核用以标识一个特定进程正在访问的文件
进程间通信方式中大多数也是通过文件描述符进行操作的
文件描述符跟文件指针类似,也是用于标识一个文件,对文件操作需要使用文件描述符
当一个程序运行时,操作系统会自动为当前程序创建三个文件描述符
0 标准输入文件描述符
1 标准输出文件描述符
2 标准错误输出文件描述符
2.文件io常用API
2.1 open()/close()
#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);
功能:
打开或者创建一个文件
参数:
pathname:
文件名,不添加路径默认为当前路径
flags:
标志位
三者必须有一个:
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
其他标志位:
如果有多个属性,需要用 位逻辑或 连接
O_APPEND 文件存在以追加方式打开文件
O_CREAT 文件不存在则创建
O_EXCL 一般与O_CREAT一起使用 O_EXCL|O_CREAT,如果文件存在则报错,文件不存在则创建
O_TRUNC 如果文件存在则清空
mode:
如果第二个参数指定了O_CREAT,就需要这个参数,否则不需要这个参数主要用于设置当前文件的访问权限,一般使用一个三位的八进制数来设置,分别设置用户主、用户组以及其他用户对当前文件的操作权限,分别设置读、写、执行权限,一般设置为0664
返回值:
成功:
文件描述符
失败:
-1
#include <unistd.h>
int close(int fd);
功能:
关闭文件描述符
参数:
fd:
文件描述符
返回值:
成功:
0
失败:
-1
2.2对比fopen和open函数的标志位:
fopen | open | 含义 |
r | O_RDONLY | 以只读的方式打开文件,如果文件不存在则报错 |
r+ | O_RDWR | 以读写的方式打开文件,如果文件不存在则报错 |
w | O_WRONLY | O_CREAT | O_TRUNC, 0664 | 以只写的方式打开文件,如果文件不存在则创建,如果文件存在则清空 |
w+ | O_RDWR | O_CREAT | O_TRUNC, 0664 | 以读写的方式打开文件,如果文件不存在则创建,如果文件存在则清空 |
a | O_WRONLY | O_CREAT | O_APPEND, 0664 | 以只写的方式打开文件,如果文件不存在则创建,如果文件存在则追加 |
a+ | O_RDWR | O_CREAT | O_APPEND, 0664 | 以读写的方式打开文件,如果文件不存在则创建,如果文件存在则追加 |
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define ERRLOG(errmsg) do{perror(errmsg);return -1;}while(0)
int main(int argc, char const *argv[])
{
//使用open函数创建或者打开一个文件
int fd;
// if((fd = open("file.txt", O_RDONLY)) == -1)
//if((fd = open("file.txt", O_RDONLY | O_CREAT | O_TRUNC, 0664)) == -1)
//以只写的方式打开文件,如果文件存在则报错,如果文件不存在则创建
if((fd = open("file.txt", O_WRONLY | O_EXCL | O_CREAT, 0664)) == -1)
{
ERRLOG("open error");
}
//关闭文件描述符
close(fd);
return 0;
}
2.3 read()
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:
从文件中读取内容
参数:
fd:
文件描述符
buf:
保存读取的数据
conut:
要读取的字节数
返回值:
成功:
实际读取的字节数
失败:
-1
如果文件读完,返回0
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#define err_log(err) do{perror(err);return -1;}while(0)
int main(int argc, char const *argv[])
{
//从终端读取数据
//现象类似fgets
#if 0
char buf[32] = {0};
read(0, buf, 32);
printf("buf = %s\n", buf);
#endif
//从文件中读取数据
int fd;
if((fd = open(argv[1], O_RDONLY)) == -1)
{
err_log("open error");
}
//注意:
//read读取文件内容时,如果文件内容足够,第三个参数是n,就会读取
//n个字节,不会预留\0的位置,也不会说像fgets一样遇到行结束符就结束
//一定要注意read的返回值表示实际读取的个数,往往文件最后一次读取的数据
//都小于第三个参数设置的值
char buf[32] = {0};
ssize_t bytes;
if((bytes = read(fd, buf, 20)) == -1)
{
err_log("read error");
}
printf("bytes = %ld, buf = [%s]\n", bytes, buf);
//清空内存区域内容memset bzero
// memset(buf, 0, sizeof(buf));
bzero(buf, sizeof(buf));
if((bytes = read(fd, buf, 20)) == -1)
{
err_log("read error");
}
printf("bytes = %ld, buf = [%s]\n", bytes, buf);
close(fd);
return 0;
}
2.4 write()
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:
向文件写入数据
参数:
fd:
文件描述符
buf:
要写入的数据
conut:
写入的字节数
返回值:
成功:
写入的字节数
失败:
-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
//向终端写入数据
// write(1, "hello world\n", 12);
//向文件写入数据
int fd;
if((fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0664)) == -1)
{
perror("open error");
return -1;
}
write(fd, "hello world\n", 12);
write(fd, "nihao beijing\n", strlen("nihao beijing\n"));
// char buf[32] = {0};
// read(fd, buf, 32);
// printf("buf = %s\n", buf);
return 0;
}
2.5 练习:实现cp命令的功能
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
//练习:实现cp命令的功能
int main(int argc, char const *argv[])
{
//打开或者创建好两个文件
int fd_r, fd_w;
if((fd_r = open(argv[1], O_RDONLY)) == -1)
{
perror("open error");
return -1;
}
if((fd_w = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
{
perror("open error");
return -1;
}
//从一个文件中读取数据写入另一个文件
char buf[32] = {0};
ssize_t bytes;
while(1)
{
//从源文件中读取数据
bytes = read(fd_r, buf, sizeof(buf));
//当源文件内容读取完毕,循环结束
if(bytes == 0)
{
break;
}
//将读取到的数据写入到目的文件
write(fd_w, buf, bytes);
}
printf("cp done\n");
return 0;
}
2.6 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 当前位置
SEEK_END 末尾位置
返回值:
成功:
当前修改之后的文件的偏移值
失败:
-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
//注意:如果open函数的第二个参数包含O_APPEND,
//写操作的偏移值是无法改变的,永远都在文件末尾,
//但是读操作没有影响
int fd;
if((fd = open(argv[1], O_RDWR | O_CREAT | O_APPEND, 0664)) == -1)
{
perror("open error");
return -1;
}
write(fd, "hello world\n", 12);
write(fd, "nihao beijing\n", strlen("nihao beijing\n"));
//获取当前文件的偏移值
printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));
//修改文件的偏移值
lseek(fd, 0, SEEK_SET);
printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));
char buf[64] = {0};
read(fd, buf, 64);
printf("buf = %s\n", buf);
puts("**********************");
lseek(fd, -6, SEEK_END);
printf("offset = %ld\n", lseek(fd, 0, SEEK_CUR));
write(fd, "123456789\n", strlen("123456789\n"));
lseek(fd, 0, SEEK_SET);
memset(buf, 0, 64);
read(fd, buf, 64);
printf("buf = %s\n", buf);
return 0;
}
2.7 文件描述符的创建问题
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
//文件描述符是从小到大依次递增创建的,
//但是如果有文件描述符关闭,则新创建的文件描述符
//的值会先等于最小的没有使用的值,然后在依次递增创建
//所以最后创建的文件描述符不一定是最大的
int fd1 = open("file.txt", O_RDONLY);
int fd2 = open("file.txt", O_RDONLY);
int fd3 = open("file.txt", O_RDONLY);
int fd4 = open("file.txt", O_RDONLY);
int fd5 = open("file.txt", O_RDONLY);
int fd6 = open("file.txt", O_RDONLY);
printf("fd1 = %d\n", fd1);
printf("fd2 = %d\n", fd2);
printf("fd3 = %d\n", fd3);
printf("fd4 = %d\n", fd4);
printf("fd5 = %d\n", fd5);
printf("fd6 = %d\n", fd6);
close(fd3);
int fd7 = open("file.txt", O_RDONLY);
printf("fd7 = %d\n", fd7);
int fd8 = open("file.txt", O_RDONLY);
printf("fd8 = %d\n", fd8);
return 0;
}