C/CPP文件操作(1)-fopen和open比较
1. fopen和open比较
不多说,我们先来看下下面的两段代码:
open系列函数:
1 int main(int argc, char *argv[]) 2 { 3 int fd; 4 char buf[32]; 5 int size = sizeof(buf); 6 fd = open("/home/liujun/Work/Study/CPP/File/test.txt", O_RDONLY); 7 if (fd < 0) { 8 printf("Failed to open file \n"); 9 } 10 memset(buf, 0, size); // 特别重要,避免乱码 11 read(fd, buf, size - 1); // 要小于buf的size,避免出现乱码 12 close(fd); 13 printf("************ %s", buf); 14 return 0; 15 }
fopen系列函数:
int main(int argc, char *argv[]) { FILE *fp; char buf[32]; int size = sizeof(buf); fp = fopen("/home/liujun/Work/Study/CPP/File/test.txt", "rb"); if (fp == nullptr) { printf("Failed to open file \n"); } memset(buf, 0, size); // 特别重要,避免乱码 fread(buf, size - 1, 1, fp); fclose(fp); printf("************ %s", buf); return 0; }
上述两段代码的作用和逻辑几乎一模一样,都是从一个文件里面读取信息并打印出来,虽然使用的逻辑不一样,但是这两个系列的函数却有着很大的不同,主要有一下几点
open是系统调用,返回的是文件句柄(后面会讲述什么是文件句柄)
fopen是ANSIC标准中的C语言库函数,是对open的封装(Linux下),所以fopen可移植,open不能,返回指向FILE的文件指针
fopen在用户态下就有缓存机制,因此在进行read和write的时候减少了用户态和内核态的切换,open没有缓存机制,每次读操作都直接从文件系统中获取数据,每次都需要进行内核态和用户态的切换
2. 文件描述符和文件指针
文件描述符:当我们调用open打开一个文件时,open会返回一个文件描述符fd,fd只是一个小整数,起到一个索引的作用。每个进程在PCB(Process Control Block)即进程控制块中都保存着一份文件描述符表,文件描述符就是这个表的索引,文件描述表中每个表项都有一个指向已打开文件的指针(该指针指向file结构体),现在我们明确一下:已打开的文件在内核中用file结构体表示,文件描述符表中的指针指向file结构体,file结构体如下所示:
1 struct file { 2 mode_t f_mode; // 文件模式,是读是写 3 loff_t f_pos; // 当前读写位置 4 unsigned short f_flags; 5 unsigned short f_count; 6 off_t f_reada; 7 struct file *f_next, *f_prev; 8 int f_owner; /* pid or -pgrp where SIGIO should be sent */ 9 struct inode * f_inode; 10 struct file_operations * f_op; 11 unsigned long f_version; 12 void *private_data; /* needed for tty driver, and maybe others */ 13 };
文件指针:当我们调用fopen打开一个文件时,fopen会返回一个文件指针,该指针指向进程用户区中的一个被称为FILE结构的数据结构,该结构体包含所打开文件的文件描述符和其它的缓存级位置信息,具体如下图所示。
1 struct FILE 2 { 3 char *_ptr;//文件输入的下一个位置 4 int _cnt;//当前缓冲区的相对位置 5 char *_base;//指基础位置(文件的起始位置) 6 int _flag;//文件标志 7 int _file;//文件描述符 8 int _charbuf;//检查缓冲区状况,如果缓冲区则不读取 9 int _bufsiz;//文件的大小 10 char *_tmpfname;//临时文件名 11 };
FILE包含文件描述符,或者说是对文件描述符的一种封装,且两者可以相互转化
int fileno(FILE *stream); // FILE -> fd
FILE *fdopen(int fd, const char *mode); fd -> FILE