参数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中的mode权限是 权限 & ~umask(指定权限和umask取反相与)得到最后结果
第一个0是代表八进制,其余是权限,前两个取反都是1,任何数与1相与都是本身.
最后一个是2取反就是 r-x 我们指定的权限最后一位是4也就是 r--
r-x & r-- = r-- 也就是创建文件对于其他用户只有读权限
fd输出的是-1表示失败
文档就是错误返回-1,并设置errno
我们可以通过返回的errno,并使用sererrno(errno)显示详细信息
ssize_t 有符号整形, fd 文件描述符, buf 读取空间, size_t(无符号整形) count 读取空间的大小(字节数)
成功返回读取到的字节数,当返回0时代表读取到文件结尾
失败返回-1,设置errno
参数和read一样,缓存区是const表示写入时不能更改, count 写入的字节数
成功返回写入的字节数
失败返回-1,设置errno
实现cp拷贝
cp aaa bbb (aaa赋值bbb)
#include
#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,则显示错误信息并结束程序.
上面设置非阻塞时,无限期等待,显然不可取.(阻塞的话更是无限期的等待了)
这个时候需要考虑的时普通文件为什么没有非阻塞选项.
普通文件在读取时,无论文件有多大,终于读完的时候,而设备(网络)文件会一直等待(也就是阻塞).
处理这个问题,常用的就是超时.其实就是等待一定时间,如果没有数据,则退出程序.进行后续操作.
#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];
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
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;
}
写文件的时候,文件指针始终在文件尾部
在读取文件的时候,依然是从文件尾部开始读取的.所以不将文件指针重新设置位置,就会读取不到数据.
这个原因是,没有读取回车(换行符)
#include
#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操作,
#include
#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;
}
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完全一样,就不再叙述了
成功返回0,失败返回-1设置errno
mode
R_OK 可读
W_OK 可写
X_OK 可执行
F_OK 文件存在
#include
#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
参数就是两个文件
成功返回0,失败返回-1设置errno
头文件 #include
原型 int unlink(const char *pathname);
成功返回0
失败返回-1,设置errno
文件目录项是记录inode,和文件名等的一个结构体
Linux下删除文件就是不断的将st_nlink -1 直到0.没有目录项对应的文件,并且打开该文件的进程关闭,就会被系统释放(时间不确定,是系统自己的机制)其实就是,开打一个文件,没有关闭即使这个文件删除了,进程中这个文件仍然存在于缓冲区
头文件 #include
int symlink(const char *target, const char *linkpath);
成功返回0
失败返回-1,设置errno
符号链接大小,就是创建的路径几个字符就是多大
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一样,用指针名就行了,无需了解细节
#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]; //文件名
};
#include
#include
int mkdir(const char *pathname, mode_t mode);
成功返回0
失败返回-1,设置errno
mode 文件权限(八进制0777)
#include
#include
void rewinddir(DIR *dirp);
#include
long telldir(DIR *dirp);
成功返回与dirp相关的目录当前读写位置(从起始位置的下一个读取位置的偏移量)
失败返回-1,设置errno
void seekdir(DIR *dirp, long loc);
loc一般由telldir函数的返回值来决定