文件IO和标准IO的区别
文件IO 和 标准IO的概念
- 文件I/O
什么是文件I/O?
posix(可移植操作系统接口)定义的一组函数
不提供缓冲机制,每次读写操作都引起系统调用
没有流指针不能用刷新流和定位流的函数
核心概念是文件描述符
访问各种类型文件
Linux下, 标准IO基于文件IO实现
文件I/O是操作系统封装了一系列open、close、write、read等API函数构成的一套用来读、写文件的接口供应用程序使用,通过这些接口可以实现对文件的读写操作,但是效率并不是最高的。
文件I/O是采用系统直接调用的方式,因此当使用这些接口对文件进行操作时,就会立刻触发系统调用过程,即向系统内核发出请求之后,系统内核会收到执行相关代码处理的请求,决定是否将操作硬件资源或返回结果给应用程序。
- 标准I/O
标准IO:应用层C语言库函数提供了一些用来做文件读写的函数列表,叫标准IO。标准IO有一系列的C库函数构成(fopen,fclose,fwrite,fread),这些标准IO函数其实是由文件IO封装而来的(fopen内部还是调用了open);,我们通过fwrite写入的内容不是直接进入内核中的buf,而是先进入应用层标准IO库自己维护的buf中,然后标准IO库自己根据操作系统单次write的最佳count来选择好的时机来完成write到内核中的buf中。因此,标准I/O封装了底层系统调用更多的调用函数接口。
文件I/O和标准I/O的本质区别
-
**缓冲区:**标准I/O函数接口在对文件进行操作时,首先操作缓存区,等待缓存区满足一定的条件时,然后再去执行系统调用,真正实现对文件的操作。 而文件I/O不操作任何缓存区,直接执行系统调用。
-
**系统开销:**使用标准I/O可以减少系统调用的次数,提高系统效率。例如,将数据写入文件中,每次写入一个字符。采用文件I/O的函数接口,每调用一次函数写入字符就会产生一次系统调用。而执行系统调用时,Linux必须从用户态切换到内核态,处理相应的请求,然后再返回到用户态,如果频繁地执行系统调用会增加系统的开销。
-
**执行效率:**采用标准I/O的函数接口,每调用一次函数写入字符,并不着急将字符写入文件,而是放到缓存区保存,之后每一次写入字符都放到缓存区保存。直到缓存区满足刷新的条件(如写满)时,再一并将缓存区中的数据写入文件,执行一次系统调用完成此过程,这样便很大程度地减少了系统的调用次数,提高了执行效率。
文件I/O
文件描述符
- 每个打开的文件都对应一个文件描述符。
- 文件描述符是一个非负整数。Linux为程序中每个打开的文件分配一个文件描述符。
- 文件描述符从0开始分配,依次递增。
- 文件IO操作通过文件描述符来完成。
0, 1, 2 的含义?
0标准输入 1标准输出 2标准错误
打开的磁盘文件从3开始
open函数
open函数用来创建或打开一个文件
#include <fcntl.h>
int open(const char *pathname, int flags); //打开文件两个参数
int open(const char *pathname, int flags, mode_t mode); //创建文件三个参数
- 成功时返回文件描述符;出错时返回EOF
- 打开文件时使用两个参数
- 创建文件时第三个参数指定新文件的权限,(只有在建立新文件时有效)此外真正建文件时的权限会受到umask 值影响,实际权限是mode-umaks(umaks普通用户为0002)
- 可以打开设备文件,但是不能创建设备文件(创建设备mknode 属于驱动部分)
标准IO和文件IO的权限对比
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AWzM35gF-1666848409468)(https://raw.githubusercontent.com/zfr010503/huaqing/master/img/202210271324710.png)]
注意:
- umask :用来设定文件或目录的初始权限
- 文件和目录的真正初始权限
- 文件或目录的初始权限 = 文件或目录的最大默认权限 - umask权限
示例1:
以只写方式打开文件1.txt。如果文件不存在则创建,如果文件存在则清空:
int fd;
if ((fd = open(“1.txt”, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
perror(“open”);
return -1;
}
示例2:
以读写方式打开文件1.txt。如果文件不存在则创建,如果文件存在则报错:
int fd;
if ((fd = open(“1.txt”, O_RDWR|O_CREAT|O_EXCL, 0666)) < 0) {
if (errno == EEXIST) {
perror(“exist error”);
} else {
perror(“other error”);
}
}
close函数
close函数用来关闭一个打开的文件
#include <unistd.h>
int close(int fd);
- 成功时返回0;出错时返回EOF
- 程序结束时自动关闭所有打开的文件
- 文件关闭后,文件描述符不再代表文件
read函数
read函数用来从文件中读取数据
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
- 成功时返回实际读取的字节数;出错时返回EOF
- 读到文件末尾时返回0
- fd是读取的文件
- buf是接收数据的缓冲区
- count是读取的长度,不应超过buf大小
示例:
从指定的文件(文本文件)中读取内容并统计大小
int main(int argc, char *argv[]) {
{
int fd, n, total = 0;
char buf[64];
if (argc < 2) {
printf(“Usage : %s <file>\n”, argv[0]); return -1;
}
if ((fd = open(argv[1], O_RDONLY)) < 0) {
perror(“open”); return -1;
}
while ((n = read(fd, buf, 64)) > 0) {
total += n;
}
write函数
write函数用来向文件写入数据
#include <unistd.h>
ssize_t write(int fd, void *buf, size_t count);
- 成功时返回实际写入的字节数;出错时返回EOF
- buf是发送数据的缓冲区
- count不应超过buf大小
示例
将键盘输入的内容写入文件,直到输入quit
int fd;
char buf[20];
if ((fd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
perror(“open”); return -1;
}
while (fgets(buf, 20, stdin) != NULL) {
if (strcmp(buf, “quit\n”) == 0) break;
write(fd, buf, strlen(buf));
}
lseek函数(用来定位文件)
#include <unistd.h>
off_t lseek(int fd, off_t offset, intt whence);
-
成功时返回当前的文件读写位置;出错时返回EOF
-
参数offset和参数whence同标准IO的fseek完全一样
-
whence参数:SEEK_SET/SEEK_CUR/SEEK_END
SEEK_SET:从距文件开头 offset 位移量为新的读写位置
SEEK_CUR:以目前的读写位置往后增加 offset 个位移量
SEEK_END:将读写位置指向文件尾后再增加 offset 个位移量
- offset参数:偏移量,可正可负
练习题
题目
使用文件IO实现“每隔1秒向文件1.txt写入当前系统时间,行号递增”
代码
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
int fp,ret;
time_t ctime;
struct tm *ctimestr;
int linecount = 0;
char buf[32];
fp=open("1.txt",O_RDWR | O_CREAT | O_APPEND, 0664);
if(fp < 0){
perror("fopen");
return 0;
}
//calculate test.txt line
while((ret = read(fp,buf,22)) != 0){ //22为每行的字符数
linecount++;
}
while(1){
ctime = time(NULL);
//printf("ctime=%d\n",(int)ctime);
ctimestr = localtime(&ctime);
printf("%04d-%02d-%02d %02d:%02d:%02d\n",ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,
ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
sprintf(buf,"%d,%04d-%02d-%02d %02d:%02d:%02d\n",linecount,ctimestr->tm_year+1900,ctimestr->tm_mon+1,ctimestr->tm_mday,
ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
if ((ret = write(fp,buf,strlen(buf))) < 0){
perror("write");
close(fp);
}
//fflush(fp);
linecount++;
sleep(1);
}
close(fp);
}
->tm_mday,
ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
if ((ret = write(fp,buf,strlen(buf))) < 0){
perror(“write”);
close(fp);
}
//fflush(fp);
linecount++;
sleep(1);
}
close(fp);
}