参数pathname文件名
参数flags为访问方式的宏:O_RDonLY(只读),O_WRonLY(只写),O_RDWR(读写)这三个是必须加的
O_APPEND(追加),O_CREAT(创建),O_EXCL(是否存在),O_TRUNC(截断,普通文件写操作截断为0),O_NonBLOCK(非阻塞,设置之后后面任何IO操作都不会阻塞)
参数mode_t mode是一个八进制数,0777
返回值是一个文件描述符
案例 open打开文件文件要已存在
open创建文件,文件存在打开,不存在创建. open文件存在打开,截断(清空)文件,不存在创建open中的mode权限是 权限 & ~umask(指定权限和umask取反相与)得到最后结果
第一个0是代表八进制,其余是权限,前两个取反都是1,任何数与1相与都是本身.
最后一个是2取反就是 r-x 我们指定的权限最后一位是4也就是 r--
r-x & r-- = r-- 也就是创建文件对于其他用户只有读权限
open函数出错fd输出的是-1表示失败
文档就是错误返回-1,并设置errno
我们可以通过返回的errno,并使用sererrno(errno)显示详细信息
read和write函数 函数原型 read
ssize_t 有符号整形, fd 文件描述符, buf 读取空间, size_t(无符号整形) count 读取空间的大小(字节数)
成功返回读取到的字节数,当返回0时代表读取到文件结尾
失败返回-1,设置errno
write参数和read一样,缓存区是const表示写入时不能更改, count 写入的字节数
成功返回写入的字节数
失败返回-1,设置errno
案例实现cp拷贝
cp aaa bbb (aaa赋值bbb)
#include复习一下C++文件读写#include #include #include using namespace std; int main(int argc,char** argv) { // 命令行参数argv[0]是文件名 int fd = open(argv[1], O_RDONLY); if(fd == -1) { //标准错误可以自动显示errno信息 //cerr是ostream类自己的错误信息 perror("int fd = open(argv[1], O_RDONLY);"); exit(1); } // 文件不存在创建文件 文件存在截断文件 int fd2 = open(argv[2], O_WRonLY | O_CREAT | O_TRUNC, 0664); if(fd2 == -1) { perror("int fd2 = open(argv[2], O_WRonLY | O_CREAT | O_TRUNC, 0664);"); exit(1); } int read_count; char buf[1024]; // 读取成功返回读取到的字节数, 读取到文件尾部返回0, 返回-1表示失败 while((read_count = read(fd,buf,sizeof(buf))) != 0) { if(read_count == -1) { perror("while((read_count = read(fd,buf,sizeof(buf))) != 0)"); exit(1); } // 向文件中写入读取到的字节 // read_count 读到多少写入多少 write(fd2,buf,read_count); } //关闭文件和打开文件一起写,不然真的会忘!!! close(fd); close(fd2); return 0; }
#include#include #include using namespace std; int main(int argc,char** argv) { fstream fin(argv[1], ios::in); if (!fin) { cerr << "fstream fin(argv[1], ios::in)"; exit(1); } // 文件不存在创建,文件存在清空 fstream fout(argv[2], ios::out); if (!fout) { cerr << "fstream fout(argv[2], ios::out)"; exit(1); } string str; //getline可以读取空格但是会舍弃换行符 while (getline(fin,str)) { fout << str << endl; } fin.close(); fout.close(); return 0; }
其实我这里体现不出来,主要是键盘不舒服不爱敲.
直接说结论C++的函数会比系统函数要快
无论哪种方式,都要经过内核在由内核写到磁盘.系统函数一次写入1024字节,而C++的函数一次写入4096字节,这是因为系统函数是系统级别的缓冲(每次都直接写入到内核一次写1024字节,系统默认的最佳IO默认一般是4k,也就是说写满4k在写入到磁盘(这个不准确,因为内核怎么写入到磁盘我也不知道,但是就是这个意思))而C++函数有自己函数的缓存(也就是用户级缓冲)(它是在自己的缓存中直接写够4K=4096byte,然后一次性的交给内核,在由内核写入磁盘)
而从用户到内核的时间,就是C++函数比系统函数快的时间(当然了这个不只是C++才有的,不负责任的说主流的语言都是这样)
系统函数和库函数存在的是使用场景不同,而不是谁的效率高
文件描述符一个进程最多可以打开1024个文件(0-1023),系统会自动选择可用的最小的文件描述符
阻塞,非阻塞常规文件不会阻塞,只有网络文件和设备文件才会阻塞,/dev/tty终端文件 /dev设备目录
阻塞是文件的属性,并不是函数的,文件属性可以修改.
#include#include #include #include #include #include #include using namespace std; int main(int argc, char** argv) { char buf[9]; int read_count; // 读取标准输入 如果没有输入会造成阻塞 read_count = read(STDIN_FILENO, buf, 9); if (read_count == -1) { perror("if (read_count = read(STDIN_FILENO, buf, 9) == -1)"); exit(1); } // 写入标准输出 write(STDOUT_FILENO, buf, read_count); return 0; }
没有输入会一直停留在这个界面
非阻塞处理(死循环演示效果)当设备或网络文件设置非阻塞时,返回-1且errno=EAGIN或EWOULDBLOCK(这两个值是一样的)说明文件中无数据,而不是打开失败
#include#include #include #include #include #include #include using namespace std; int main(int argc, char** argv) { // /dev/tty 是终端 int fd = open("/dev/tty", O_RDonLY | O_NONBLOCK); if (fd == -1) { perror("int fd = open( / dev / tty, O_RDonLY | O_TRUNC);"); exit(1); } int read_count; char buf[9]; while (true) { read_count = read(fd, buf, 9); if (read_count == -1 && errno == EAGAIN) { cout << "文件无数据" << endl; sleep(5); } else if (read_count == -1 && errno != EAGAIN) { perror("read_count = read(fd, buf, 64);"); exit(1); } else { write(STDOUT_FILENO, buf, read_count); close(fd); break; } } return 0; }
哦,对了,代码头文件是我一直用的一个程序改的,因为懒嘛.具体的头文件前面我都说过不影响哈.
read返回-1,且errno是EAGAIN则显示无数据,停止五秒,等待输入,如果没有输入,依然显示无数据
有输入显示数据并跳出循环.
如果返回是-1,errno不是EAGAIN,则显示错误信息并结束程序.
非阻塞处理(只是处理,而非解决问题,解决这个问题的方法是响应模式.这个目前先不谈)上面设置非阻塞时,无限期等待,显然不可取.(阻塞的话更是无限期的等待了)
这个时候需要考虑的时普通文件为什么没有非阻塞选项.
普通文件在读取时,无论文件有多大,终于读完的时候,而设备(网络)文件会一直等待(也就是阻塞).
处理这个问题,常用的就是超时.其实就是等待一定时间,如果没有数据,则退出程序.进行后续操作.
#includefcntl(改变一个已经打开文件的访问控制属性)#include #include #include #include #include #include using namespace std; int main(int argc, char** argv) { // /dev/tty 是终端 int fd = open("/dev/tty", O_RDonLY | O_NONBLOCK); if (fd == -1) { perror("int fd = open( / dev / tty, O_RDonLY | O_TRUNC);"); exit(1); } int read_count; char buf[9]; int count = 3; while (count) { count--; read_count = read(fd, buf, 9); if (read_count == -1 && errno == EAGAIN) { cout << "文件无数据" << endl; sleep(2); } else if (read_count == -1 && errno != EAGAIN) { perror("read_count = read(fd, buf, 64);"); exit(1); } else { write(STDOUT_FILENO, buf, read_count); close(fd); break; } } if (count == 0) { cout << "超时" << endl; } return 0; }
这个函数十分强大,内容特别多.这里只介绍两个F_GETFL和F_SETFL两个命令
参数 int cmd,就是这两个命令.
失败返回-1
成功返回一个位图(就是要给二进制位表,每一个位都有各自的涵义)
#include#include #include #include #include #include #include using namespace std; int main(int argc, char** argv) { // 得到文件属性(状态) int flag = fcntl(STDIN_FILENO, F_GETFL); if (flag == -1) { perror("int flag = fcntl(STDIN_FILENO, F_GETFL);"); exit(1); } // 设置文件属性 flag |= O_NONBLOCK; // 添加非阻塞状态 fcntl(STDIN_FILENO, F_SETFL, flag); if (flag == -1) { perror("fcntl(STDIN_FILENO, F_SETFL, flag);"); exit(1); } int read_count; char buf[9]; int count = 3; while (count) { count--; read_count = read(STDIN_FILENO, buf, 9); if (read_count == -1 && errno == EAGAIN) { cout << "文件无数据" << endl; sleep(2); } else if (read_count == -1 && errno != EAGAIN) { perror("read_count = read(fd, buf, 64);"); exit(1); } else { write(STDOUT_FILENO, buf, read_count); break; } } if (count == 0) { cout << "超时" << endl; } return 0; }
效果是一样的
位或
010 |= 001 结果就是011,和1进行按位或运算结果都是1
lseek修改文件偏移量off_t 是矢量
offset 偏移量
whence 偏移量起始位置
SEEK_SET 起始位置
SEEK_CUR 当前位置
SEEK_END 末尾位置
成功返回从文件起始位置开始的偏移量
失败返回-1设置errno
#include#include #include #include #include #include #include #include using namespace std; int main(int argc, char** argv) { int fd = open(argv[1], O_RDWR | O_CREAT, 0664); if (fd == -1) { perror("int fd = open(argv[1], O_RDWR | O_CREAT);"); exit(1); } char write_buf[1024] = "asfjljfkldjfkljf"; //将内容写入文件 write(fd, write_buf, strlen(write_buf)); // 不添加这条语句 会读取不到 文件指针是共用一个的 lseek(fd, 0, SEEK_SET); int read_count; char read_buf[1024]; // 读取文件 while (read_count = read(fd, read_buf, 1024)) { if (read_count == -1) { perror("while (read_count = read(fd, ch, 1024))"); exit(1); } cout << read_buf << endl; } close(fd); return 0; }
写文件的时候,文件指针始终在文件尾部
在读取文件的时候,依然是从文件尾部开始读取的.所以不将文件指针重新设置位置,就会读取不到数据.
这个原因是,没有读取回车(换行符)
lseek可以得到文件大小#includelseek还可以拓展文件#include #include #include #include #include #include #include using namespace std; int main(int argc, char** argv) { int fd = open("test.txt", O_RDWR | O_CREAT | O_TRUNC, 0664); if (fd == -1) { perror("int fd = open(test.txt, O_RDWR | O_CREAT);"); exit(1); } char write_buf[1024] = "asfjljfkldjfkljf"; //将内容写入文件 write(fd, write_buf, strlen(write_buf)); // 返回值说过,将偏移量设置到文件尾部返回的就是文件的大小 int file_size = lseek(fd, 0, SEEK_END); cout << "file size :" << file_size << endl; close(fd); return 0; }
#include#include #include #include #include #include #include #include using namespace std; int main(int argc, char** argv) { int fd = open("test.txt", O_RDWR); if (fd == -1) { perror("int fd = open(test.txt, O_RDWR);"); exit(1); } // 将文件指针设置到文件末尾,在进行偏移,偏移出来的就是拓展出的大小 int file_size = lseek(fd, 24, SEEK_END); cout << "file size :" << file_size << endl; close(fd); return 0; }
不过这是冒充的,真正的拓展文件需要IO操作,
truncate拓展文件#includeftruncate拓展文件#include #include #include #include #include #include #include using namespace std; int main(int argc, char** argv) { int flag = truncate("test.txt", 500); if (flag == -1) { perror("int flag = truncate(test.txt, 500);"); exit(1); } return 0; }
#include#include #include #include #include #include #include #include using namespace std; int main(int argc, char** argv) { int fd = open("test.txt", O_RDWR); if (fd == -1) { perror("int fd = open(test.txt, O_RDWR);"); exit(1); } int flag = ftruncate(fd, 1000); if (flag == -1) { perror("int flag = ftruncate(fd, 1000);"); exit(1); } return 0; }
stat(获取文件属性)
struct stat* statbuf是一个传出参数,(就是C时,改变一个变量用指针,改变一个指针用二级指针)
结构体中的信息,就是一些文件的属性,终端命令ll时的一些信息
st_mode,可以判断是什么文件
#include#include #include #include #include #include #include #include using namespace std; int main(int argc, char** argv) { struct stat stat_buf; int fd = stat("test.txt", &stat_buf); if (fd == -1) { perror("int fd = stat(test.txt,&stat_buf);"); exit(1); } cout << "file size:" << stat_buf.st_size << endl; // is是宏函数,返回值是bool(其实系统/库宏函数的返回值基本都是bool值) if (S_ISREG(stat_buf.st_mode)) { cout << "regular file" << endl; } // if宏定义,按位与(任何数和1相与都是本身) if ((stat_buf.st_mode & S_IFMT) == S_IFREG) { cout << "regular file" << endl; } return 0; }
S_IFMT是文件掩码,可以理解成st_mode是要个16位的位图,前四位表示文件类型,后九位表示文件权限,中间还有三位是干什么的记不住了....(这个也不准确啊,但是大致就是这个意思!!!)
但是当文件时链接文件的时候,会穿透直接显示源文件的属性.
stat函数默认可以穿透链接文件,lstat函数不会穿透.
lstat函数参数和stat完全一样,就不再叙述了
access检测文件是否存在或是否拥有某些权限成功返回0,失败返回-1设置errno
mode
R_OK 可读
W_OK 可写
X_OK 可执行
F_OK 文件存在
#includechmod修改文件访问权限#include using namespace std; int main(int argc,char** argv) { int flag = access("test.txt",F_OK); if(!flag) { cout << "test.txt 存在" << endl; } return 0; }
mode 参数的值
成功返回0,失败返回-1设置errno
link创建硬链接参数就是两个文件
成功返回0,失败返回-1设置errno
unlink(删除一个文件的目录项)头文件 #include
原型 int unlink(const char *pathname);
成功返回0
失败返回-1,设置errno
文件目录项是记录inode,和文件名等的一个结构体
Linux下删除文件就是不断的将st_nlink -1 直到0.没有目录项对应的文件,并且打开该文件的进程关闭,就会被系统释放(时间不确定,是系统自己的机制)其实就是,开打一个文件,没有关闭即使这个文件删除了,进程中这个文件仍然存在于缓冲区
symlink(创建符号链接)头文件 #include
int symlink(const char *target, const char *linkpath);
成功返回0
失败返回-1,设置errno
符号链接大小,就是创建的路径几个字符就是多大
readlink 命令可以查看符号链接文件
readlink(获得符号链接所指向的文件名)#include
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
成功返回指向文件名的字节数
失败返回-1,设置errno
#include
int rename(const char *oldpath, const char *newpath);
成功返回0
失败返回-1,设置errno
#include
char *getcwd(char *buf, size_t size);
成功返回字符串指针,指针的值和buf一样
失败返回NULL
#include
int chdir(const char *path);
成功返回0
失败返回-1,设置errno
#include
#include
DIR *opendir(const char *name);
成功返回指向该目录的结构体指针
失败返回NULL
就像C中文件指针,FILE一样,用指针名就行了,无需了解细节
closedir(关闭一个目录)#include
#include
int closedir(DIR *dirp);
成功返回0
失败返回-1,设置errno
#include
struct dirent *readdir(DIR *dirp);
成功返回目录项结构体指针,(循环读取完毕,返回NULL不是设置errno)
失败返回NULL,设置errno
struct dirent
{
ino_t d_ino; // inode编号
off_t d_off; // 偏移量
unsigned short d_reclen; // 文件名有效长度
unsigned char d_type; // 类型(vim 打开看到的类似@*/等)
char d_name[256]; //文件名
};
mkdir(创建目录)
#include
#include
int mkdir(const char *pathname, mode_t mode);
成功返回0
失败返回-1,设置errno
mode 文件权限(八进制0777)
rewinddir(回卷目录读写位置到起始)#include
#include
void rewinddir(DIR *dirp);
telldir(获取目录流读取位置)/seekdir(设置下回读取目录的位置)#include
long telldir(DIR *dirp);
成功返回与dirp相关的目录当前读写位置(从起始位置的下一个读取位置的偏移量)
失败返回-1,设置errno
void seekdir(DIR *dirp, long loc);
loc一般由telldir函数的返回值来决定



