Linux之IO精讲

目录

引言

1 标准IO

1.1 介绍

1.2 fopen

1.3 fwrite 

1.4 fread

1.5 rename与remove

 1.6 fseek+rewind+ftell

 1.7 fprintf和fscanf

1.8 fgetc/s+fputc/s

2. 文件IO

3. 文件IO与标准IO的区别

引言

本文以文件操作方式实现学生信息管理系统来讲解IO

IO分为标准IO与文件IO,标准 IO是通用的,即标准C库写的代码可以跑在不同的系统或平台上;而Linux系统,众所周知的一句话叫做linux下,万物皆文件,它用的不是标准C库,而是GLIBC,glibc是标准库的超集,即glibc包含了C库,所以linux系统上有两套访问file的方法。

1 标准IO

1.1 介绍

功能:标准IO通过缓冲机制减少系统调用次数,实现更高的效率.

文件类型:

        常规文件(r);目标文件(d);管道文件(p);块设备文件(b);字符设备文件(c);套接字文件(s);符号链接文件(l).

流的概念:

        FILE结构体,标准IO用一个结构体类型来存放打开的文件的相关信息,FILE结构体也称流(stream),分为文本流和二进制流,其区别在于:windows中,二进制流:换行符:’\n’,文本流:换行符:\r’’\n’;而linux系统中,只有换行符:’\n’;

1.2 fopen

#include <stdio.h>

原型: FILE *fopen(const char *path, const char *mode);

path:指定文件路径和文件名 

mode:                   
        返回值:
                    返回文件指针,指向了你要求打开的文件;
                    失败:    返回NULL  并将错误原因,记录在了errno(错误码)中

示例代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 20
FILE *fp==NULL;//定义文件指针
int fopen_file(){
      fp=fopen("./test1.txt","w+");//以读写方式打开文件,若不存在则创建,写之前将文件清空;成功的话将返回文件指针
      if(fp==NULL){
          perror("fopen err");//打开文件失败
          return 0;
      }
      else{printf("打开文件成功\n");}
  
  }

  int main(){
      fopen_file();
      fclose(fp);
      fp=NULL;
  return 0;
 }

运行结果:

注意:在文件操作时,需要注意以下几点问题:1、在定义文件指针时,要将文件指针指向空;如 FILE *fp = NULL;2、文件操作完成后,需要将文件关闭,一定要注意,否则会造成文件所占用内存泄露和在下次访问文件时出现问题。3、文件关闭后,需要将文件指针指向空,这样做会防止出现游离指针,而对整个工程造成不必要的麻烦;如:fp = NULL;

1.3 fwrite 

功能:将数组或缓冲区等里的内容写入指定文件流

#include<stdio.h>

原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

const void *ptr :第一个参数为一个普通指针,可以指向一个数组地址或一个字符串地址等等,即要写入数据的地址;通常这里是做一个缓冲区buf(假如定义buffer缓冲区的大小为1024且第二个参数为1时,那么fread的第三个参数一定要小于等于1024,否则会出现错误。或许大家在学的时候记得比较清楚,实际编程中有可能会忘记。在此特别说明)

size_t size :第二个参数为这个指针或者数组单个存储单元的大小;即要被写入的每个元素的大小,以字节为单位;

size_t nmemb :第三个参数为要写入size字节的数据项的个数,可以理解为将缓冲区的内容写入stream指向的文件,共写numemb组数据,每组数据size个字节;

FILE *stream :第四个参数为文件指针

返回值:如果size为1,那么返回实际写入的字节数,也就是返回实际写入的数据数目。如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。如果该数字与 nmemb 参数不同,则会显示一个错误。

关于fwrite的个人理解(如果有错请指正):fwrite以二进制形式写入文件,里面只可以看到string类型的数据,如下图cat test1.txt:


 

1.4 fread

功能:从指定文件流读取数据

库文件:

        #include <stdio.h>

原型:

       size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数:

        void *ptr :内存区块的指针,可以是数组,变量,结构体等,ptr用来存放读取到的数据

        size_t size : 每个数据块的字节数

        size_t nmemn : 要读取的数据块的块数,即元素个数

        FILE *stream : 文件指针

size_t 是在stdio.h和stdlib.h库文件中使用tydef定义的数据类型,表示无符号整数,也即非负数,常用来表示数量;

返回值:

        返回成功读取的块数,即nmemb,如果返回值小于nmemb,对于fwrite来说肯定是发生错误,对于fread可能是读到了文件末尾,可以用ferror()或feof()检测.

示例代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 20
FILE *fp=NULL;//定义文件指针


struct Student{
	int id;
	char name[10];
	double score;
};
struct Student stu[N];//定义结构体数组
struct Student buffer;//定义结构体类型缓冲区

void fwrite_to_file(){

	fwrite(&buffer,sizeof(struct Student),1,fp);//写入一个结构体大小的数据块

}
void fread_file(){
	memset(&buffer,0,sizeof(buffer));//清空缓冲区
	fread(&buffer,sizeof(struct Student),1,fp);//从fp读出一个结构体数据块到缓冲区
	//puts("fread_file sucucess");
}
int fopen_file_null(){
	fp=fopen("./test1.txt","w+");//以读写方式打开文件,若不存在则创建,写之前将文件清空;成功的话将返回文件指针
	if(fp==NULL){
		perror("fopen err");//打开文件失败
		return 0;
	}
	else{printf("打开文件成功\n");}	
}
int fopen_file_append(){
	fp=fopen("./test1.txt","a+");//打开文件用于追加,若不存在则创建,读文件时从文件初始开始,输出时追加到末尾
	if(fp==NULL){
		perror("fopen err");//打开文件失败
		return 0;
	}
	else{printf("打开文件成功\n");}	
}
void addstu_append(){
	puts("以追加方式打开文件准备写入");
	fopen_file_append();
	memset(&buffer,0,sizeof(buffer));
	puts("please input one stu's message:id(int) name(string) score(double)");
	scanf("%d %s %lf",&buffer.id,buffer.name,&buffer.score);
	fwrite_to_file();
	fclose(fp);	
	fp=NULL;
	puts("写入成功 关闭文件");
}
int file_to_stu(){
	fopen_file_append();
	puts("读取文件");
	int index=0;
	while(!feof(fp)){//判断文件内位置指针是否为空
		fread(&stu[index],sizeof(struct Student),1,fp);
		index++;
	}
	fclose(fp);
	fp=NULL;
	puts("关闭文件");
	return index-1;
}
void outputstu(int index){
	for(int i=0;i<index;i++){
		printf("%d %s %lf",stu[i].id,stu[i].name,stu[i].score);
		printf("\n");
	}
}
int main(){
	int choice;
	int index=0;
loop:
	puts("please input your choice:");
	puts("1.追加学生  2.输出所有学生信息");
	scanf("%d",&choice);
	switch(choice){
		case 1:
			addstu_append();goto loop;
		case 2:
			index=file_to_stu();
			outputstu(index);
			goto loop;
		default:
			break;
	}
return 0;
}

运行结果:

1.5 rename与remove

这两个函数用来重命名文件和删除文件操作

rename:将文件由char*oldpath重命名为char *newpath,如果需要,将其移动在目录之间

如果newname指定的文件存在,则会被删除。\n\n如果newname与oldname不在一个目录下,则相当于移动文件。

重命名目录:如果oldname和oldname都为目录,则重命名目录。如果newname指定的目录存在且为空目录,则先将newname删除。对于newname和oldname两个目录,调用进程必须有写权限。重命名目录时,newname不能包含oldname作为其路径前缀。例如,不能将/usr更为/usr/foo/testdir,因为老名字( /usr/foo)是新名字的路径前缀,因而不能将其删除。

#include <stdio.h>

       int rename(const char *oldpath, const char *newpath);

参数:

        char *oldpath:旧的文件路径加文件名

        char *newpath: 新的文件路径加文件名

返回值: On  success,  zero is returned.  On error, -1 is returned, and errno is
       set appropriately.

remove:  deletes  a  name from the filesystem

(man手册功能描述翻译,需自行查看man手册得到更好的理解)如果已删除的名称是文件的最后一个,并且没有进程文件打开,文件已删除,并使用它的空间可重用,如果名称是文件的最后一个,但是任何进程仍然有该文件打开,该文件将保持存在直到最后一个文件.如果名称称为符号,则删除。 如果名称称为插座,FIFO或设备,则删除了名称,但是开放的过程可能会继续使用它。

 #include <stdio.h>

       int remove(const char *pathname);

参数:指定的文件路径和文件名

返回值:On  success,  zero is returned.  On error, -1 is returned, and errno is set appropriately.

程序实例:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 20
FILE *fp=NULL;//定义文件指针
char *filename="./test1.txt";
char *tmpfilename="./tmp.txt";
struct Student{
	int id;
	char name[10];
	double score;
};
struct Student stu[N];
struct Student buffer;

void fwrite_to_file(){

	fwrite(&buffer,sizeof(struct Student),1,fp);
	//memset(&buffer,0,sizeof(buffer));
}
void fread_file(){
	memset(&buffer,0,sizeof(buffer));
	//fseek(fp,0,SEEK_SET);
	fread(&buffer,sizeof(struct Student),1,fp);
	//puts("fread_file sucucess");
}
int fopen_file_null(){
	fp=fopen(filename,"w+");//以读写方式打开文件,若不存在则创建,写之前将文件清空;成功的话将返回文件指针
	if(fp==NULL){
		perror("fopen err");//打开文件失败
		return 0;
	}
	else{printf("打开文件成功\n");}	
}
int fopen_file_append(){
	fp=fopen(filename,"a+");//打开文件用于追加,若不存在则创建,读文件时从文件初始开始,输出时追加到末尾
	if(fp==NULL){
		perror("fopen err");//打开文件失败
		return 0;
	}
	else{printf("打开文件成功\n");}	
}
void addstu_append(){
	//puts("以追加方式打开文件准备写入");
	fopen_file_append();
	memset(&buffer,0,sizeof(buffer));
	puts("please input one stu's message:id(int) name(string) score(double)");
	scanf("%d %s %lf",&buffer.id,buffer.name,&buffer.score);
	fwrite_to_file();
	fclose(fp);	
	fp=NULL;
	//puts("写入成功 关闭文件");
}
int file_to_stu(){
	fopen_file_append();
	//puts("读取文件");
	int index=0;
	while(!feof(fp)){
		fread(&stu[index],sizeof(struct Student),1,fp);
		index++;
	}
	fclose(fp);
	fp=NULL;
	//puts("关闭文件");
	return index-1;
}
void outputstu(int index){
	for(int i=0;i<index;i++){
		printf("%d %s %lf",stu[i].id,stu[i].name,stu[i].score);
		printf("\n");
	}
}
void delete_stu_by_id(){
	//int index=file_to_stu();
	int del_id;
	puts("please input stu's id which you want to delete:");
	scanf("%d",&del_id);
	fopen_file_append();
	FILE *tp=fopen(tmpfilename,"w+");
	while(!feof(fp)){
		fread_file();
		if(buffer.id==del_id){
			fread_file();
		}
		if(!(buffer.id==0))
		fwrite(&buffer,sizeof(struct Student),1,tp);
	}
	fclose(fp);
	fclose(tp);
	fp=NULL;
	tp=NULL;
	remove(filename);
	rename(tmpfilename,filename);
}
int main(){
	int choice;
	int index=0;
loop:
	puts("please input your choice:");
	puts("1.追加学生  2.输出所有学生信息 3.delete_stu_by_id");
	scanf("%d",&choice);
	switch(choice){
		case 1:
			addstu_append();goto loop;
		case 2:
			index=file_to_stu();
			outputstu(index);
			goto loop;
		case 3:
			delete_stu_by_id();
			goto loop;
		default:
			break;
	}
return 0;
}

运行结果截图:

 1.6 fseek+rewind+ftell

fseek:个人理解:手动设置文件内位置指针的位置,如果执行成功,stream将指向以whence为基准,偏移offset个字 节的位置。如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置。

注意点:如果以"a"或"a+"方式打开文件,那么fseek不起作用。"a+"描述:打开一个文件,如果该文件不存在将创建文件,初始化的文件读指针位于文件的开头;对于文件的写操作,则始终将写入内容追加到文件的末尾,与文件指针没有关系,读的时候从文件开头读。

#include <stdio.h>

       int fseek(FILE *stream, long offset, int whence);

参数:

FILE *stream :文件指针

long offset: 偏移量,可以设置正负,默认正方向

int whence :

        SEEK_SET  文件开头位置

        SEEK_CUR  当前位置

         SEEK_END  文件末尾位置

返回值: 成功,返回0,否则返回其他值。

同时这里补充一个函数:#include<stdio.h>    void rewind(FILE * stream);

功能:将文件内位置指针重新指向一个流(数据流/文件)的开头,可以等同于fseek(FILE *stream,0,SEEK_SET);

无返回值.

再补充一个函数:#include <stdio.h>           long ftell(FILE *stream);

功能描述:返回文件指针相对于起始位置的偏移量,即获取文件的当前指针位置相对于文件首地址的偏移字节数。

使用场景一:随机方式存取文件时,使用fseek函数来回移动指针,通过调用ftell函数确定指针位置

使用场景二: 通过ftell计算文件的大小,打开文件后,使用fseek跳转到文件末尾,然后使用ftell来获取当前指针,打印出指针的位置,即可获取文件大小

示例代码:

#include<stdio.h>
int main(){
	FILE *fp=fopen("./test1.txt","r");//以只读方式打开文件
	printf("%ld\n",ftell(fp));//打印指针位置,注意ftell是long型
	fseek(fp,0,SEEK_END);//将指针移动至文件末尾
	printf("%ld\n",ftell(fp));//打印指针位置
	rewind(fp);//使用rewind将位置指针移动至文件开头
	printf("%ld\n",ftell(fp));//打印指针位置

	fclose(fp);
	fp=NULL;
	return 0;
}

 运行结果:

 可以看到文件的大小确实是72字节,而上图中可以看到我这里有3个学生,那么就是3个结构体大小的字节,我打印出一个结构体的大小刚好是24,乘三得到72,刚好对的上。

但是我的结构体定义如下:int id;char name[10];double score;   x64系统中int 4个字节,double 8个字节,8+4+10=22;为什么少了两个呢,这就涉及到内存或字节对齐问题了,后面我会发一篇关于字节对齐的文章链接在下面,记得插眼哦.

 先做一个粗略解释:int 4个字节,char name[10]个字节,到这里与int字节对不齐,要加两个,变成12个字节,12/4=3,紧跟的double 可以看作两个int,不用对齐,这样整个结构体就是24个字节,3个就是72.

接着走:

/*
这是一段关于更新学生信息的子功能函数,思路:输入要更新的学生信息的学生学生学号,从文件读到buffer,比对,不相符,就清空buffer,继续读,如果匹配,修改信息后写入
注意:读到匹配信息的时候,文件内位置指针已经到匹配信息的末尾,所以需要用fseek把它向前移动一个结构体的大小,然后覆盖写入
*/
void update_stu_by_id(){
	int update_id;
	puts("please input stu's id which you want to update:");
	scanf("%d",&update_id);
	fopen_file_read_jia();
	while(!feof(fp)){
		fread_file();
		if(buffer.id==update_id){
			puts("please input new message");
			scanf("%s %lf",buffer.name,&buffer.score);
			fseek(fp,-sizeof(struct Student),SEEK_CUR);
			fwrite(&buffer,sizeof(struct Student),1,fp);
			memset(&buffer,0,sizeof(buffer));
			goto loop2;
		}
	}
	puts("cannot find");
loop2:
	fclose(fp);
	fp=NULL;
}

完整示例程序:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 20
FILE *fp=NULL;//定义文件指针
char *filename="./test1.txt";
char *tmpfilename="./tmp.txt";
struct Student{
	int id;
	char name[10];
	double score;
};
struct Student stu[N];
struct Student buffer;

void fwrite_to_file(){

	fwrite(&buffer,sizeof(struct Student),1,fp);
	//memset(&buffer,0,sizeof(buffer));
}
void fread_file(){
	memset(&buffer,0,sizeof(buffer));
	//fseek(fp,0,SEEK_SET);
	fread(&buffer,sizeof(struct Student),1,fp);
	//puts("fread_file sucucess");
}
int fopen_file_read_jia(){
	fp=fopen(filename,"r+");//以读写方式打开文件,
	if(fp==NULL){
		perror("fopen err");//打开文件失败
		return 0;
	}
	else{printf("打开文件成功\n");}	
}
int fopen_file_null(){
	fp=fopen(filename,"w+");//以读写方式打开文件,若不存在则创建,写之前将文件清空;成功的话将返回文件指针
	if(fp==NULL){
		perror("fopen err");//打开文件失败
		return 0;
	}
	else{printf("打开文件成功\n");}	
}
int fopen_file_append(){
	fp=fopen(filename,"a+");//打开文件用于追加,若不存在则创建,读文件时从文件初始开始,输出时追加到末尾
	if(fp==NULL){
		perror("fopen err");//打开文件失败
		return 0;
	}
	else{printf("打开文件成功\n");}	
}
void addstu_append(){
	//puts("以追加方式打开文件准备写入");
	fopen_file_append();
	memset(&buffer,0,sizeof(buffer));
	puts("please input one stu's message:id(int) name(string) score(double)");
	scanf("%d %s %lf",&buffer.id,buffer.name,&buffer.score);
	fwrite_to_file();
	fclose(fp);	
	fp=NULL;
	//puts("写入成功 关闭文件");
}
int file_to_stu(){
	fopen_file_append();
	//puts("读取文件");
	int index=0;
	while(!feof(fp)){
		fread(&stu[index],sizeof(struct Student),1,fp);
		index++;
	}
	fclose(fp);
	fp=NULL;
	//puts("关闭文件");
	return index-1;
}
void outputstu(int index){
	for(int i=0;i<index;i++){
		printf("%d %s %lf",stu[i].id,stu[i].name,stu[i].score);
		printf("\n");
	}
}
void delete_stu_by_id(){
	//int index=file_to_stu();
	int del_id;
	puts("please input stu's id which you want to delete:");
	scanf("%d",&del_id);
	fopen_file_append();
	FILE *tp=fopen(tmpfilename,"w+");
	while(!feof(fp)){
		fread_file();
		if(buffer.id==del_id){
			fread_file();
		}
		if(!(buffer.id==0))
		fwrite(&buffer,sizeof(struct Student),1,tp);
	}
	fclose(fp);
	fclose(tp);
	fp=NULL;
	tp=NULL;
	remove(filename);
	rename(tmpfilename,filename);
}
void find_stu_by_id(){
	int find_id;
	puts("please input stu's id which you want to find:");
	scanf("%d",&find_id);
	fopen_file_append();//"a+"方式打开文件作为读的时候从文件开头读
	while(!feof(fp)){
		fread_file();
		if(buffer.id==find_id){
			puts("find him or her,follow is message:");
			printf("%d %s %lf\n",buffer.id,buffer.name,buffer.score);
			goto loop1;
		}
	}
		puts("cannot find");
loop1:
		fclose(fp);
		fp=NULL;
}
void update_stu_by_id(){
	int update_id;
	puts("please input stu's id which you want to update:");
	scanf("%d",&update_id);
	fopen_file_read_jia();
	while(!feof(fp)){
		fread_file();
		if(buffer.id==update_id){
			puts("please input new message");
			scanf("%s %lf",buffer.name,&buffer.score);
			fseek(fp,-sizeof(struct Student),SEEK_CUR);
			fwrite(&buffer,sizeof(struct Student),1,fp);
			memset(&buffer,0,sizeof(buffer));
			goto loop2;
		}
	}
	puts("cannot find");
loop2:
	fclose(fp);
	fp=NULL;
}
int main(){
	int choice;
	int index=0;
loop:
	puts("please input your choice:");
	puts("1.追加学生  2.输出所有学生信息 3.delete_stu_by_id 4.find_stu_by_id 5.update_stu_by_id");
	scanf("%d",&choice);
	switch(choice){
		case 1:
			addstu_append();goto loop;
		case 2:
			index=file_to_stu();
			outputstu(index);
			goto loop;
		case 3:
			delete_stu_by_id();
			goto loop;
		case 4:
			find_stu_by_id();
			goto loop;
		case 5:
			update_stu_by_id();
			goto loop;
		default:
			break;
	}
return 0;
}

运行结果:

 1.7 fprintf和fscanf

其实printf是将变量写入stdout输出流中,scanf从stdin输入流中读取数据并用变量接收,那么fprintf和fscanf只是将流换成文件流。

#include <stdio.h>

int fprintf(FILE *stream, const char *format, ...);

功能:格式化写入到一个文件流中,与fwrite功能类似
参数:第一个参数是文件指针,与上面的fp一样,后面是格式或字符串,与printf一样

返回值:成功返回非负值,即成功写入元素的个数,发生错误返回EOF 

int fscanf(FILE *stream, const char *format, ...);与fread功能类似,从文件中格式化读取数据,成功则返回被成功赋值的参数个数

示例代码:

//fprintf和fscanf是按照固定的格式进行读写,比较方便,但是也要注意细节,顺序和格式一定要对应
//这种写法没有考虑struct字节对齐问题,如果改变读写的顺序,会出错,可以采用按行读写的方式解决
#include<stdio.h>
char *filename="./test2.txt";
FILE *fp=NULL;

struct Student{
	int id;
	char name[10];
	double score;
}stu;
struct Student buffer;
void fprintf_to(){
	fp=fopen(filename,"w");
	fprintf(fp,"%d %s%c %lf\n",buffer.id,buffer.name,'\0',buffer.score);//向文件写入buffer的数据
	//注意这里%s%c  后面'\0',这样写是为了读的时候,%s以\0作为结束标志,否则连同后面的数据一起当作字符串读到stu.name里面
 //fprintf(fp,"\n%s%c %d %lf\n",buffer.name,'\0',buffer.id,buffer.score);//向文件写入buffer的数据
//这里如果我改变写入的顺序,由于字节对齐问题,会导致出错,所以要多注意细节,或者按照按行读写的方法
	fclose(fp);
	fp=NULL;
}
void from_fscanf(){
	fp=fopen(filename,"r");
	fscanf(fp,"%d%s%lf",&stu.id,stu.name,&stu.score);//从文件中读到结构体
	fclose(fp);
	fp=NULL;
}
void output(){
	printf("%d %s %lf\n",stu.id,stu.name,stu.score);//输出结构体内容
}
void input(){
	puts("please input:");
	scanf("%d%s%lf",&buffer.id,buffer.name,&buffer.score);//输入内容到buffer
}
int main(){
	input();
	fprintf_to();
	from_fscanf();
	output();
	return 0;
}

 运行结果:

1.8 fgetc/s+fputc/s

#include <stdio.h>

       int fgetc(FILE *stream);

功能:从指定流读取一个字符,一般形式:char ch=fgetc(fp);ch用于接收读到的字符;

返回值:读取成功返回读取到的字符,读到文件末尾或读取失败返回EOF。返回值之所以是int类型,是为了容纳这个EOF,不绝对是-1,也可以是其它负数。

其它类似的不过多介绍

man手册:

#include <stdio.h>

       int fgetc(FILE *stream);

       char *fgets(char *s, int size, FILE *stream);

fgets函数不断地读取文件遇到\n结束,然后在s字符串的尾部追加'\0',如果fgets没有读完一行, 那么本次他只会读取 size-1个char,然后再s的最后追加\0,当继续下一个fgets的时候, 继续读取

       int getc(FILE *stream);

       int getchar(void);

       int ungetc(int c, FILE *stream);

RETURN VALUE
       fgetc(),  getc(),  and getchar() return the character read as an unsigned char cast to an int or EOF on end of file or error.

       fgets() returns s on success, and NULL on error or when end of file occurs while no characters have been read.

       ungetc() returns c on success, or EOF on error.
 

#include <stdio.h>

       int fputc(int c, FILE *stream);

功能:写一个字节到文件流,成功返回字符,失败返回EOF

其它类似的不过多介绍

man手册:

     #include <stdio.h>

       int fputc(int c, FILE *stream);

       int fputs(const char *s, FILE *stream);

       int putc(int c, FILE *stream);

       int putchar(int c);

       int puts(const char *s);

RETURN VALUE
       fputc(),  putc(),  and putchar() return the character written as an unsigned char cast to an int or EOF on error.

       puts() and fputs() return a nonnegative number on success, or EOF on error.


2. 文件IO

关于文件IO其实与标准IO相差无几,最近没时间写了,先写个大纲,有时间再写的详细点
文件IO:
	fopen-->open  fread--read  fwrite--write fseek/ftell-->lseek 
	fclose-->close

	

其他接口:
	删除文件 unlink
		int unlink(filepathname)

	目录的访问:
		创建目录mkdir  删除目录rmdir
			int mkdir(目录路径和名字)
			int rmdir(目录的路径和名字)
		
		##查看内容[ 子目录 文件]
			opendir    打开指定的目录
			closedir   关闭目录
			readdir		读取目录中的每一项
先提供两篇其它博主的关于文件IO的好文章
第一篇:
https://blog.csdn.net/m0_62731496/article/details/123652525?spm=1001.2014.3001.5506

第二篇:
https://blog.csdn.net/weixin_48085453/article/details/124405542?spm=1001.2014.3001.5506

3. 文件IO与标准IO的区别

3.1 标准IO容易断点容易丢失数据,因为它读写数据经过缓存,但是它实现了跨平台的用户缓存解决方案;文件IO文件访问机制不经过操作系统内核的缓存,数据直接在磁盘和应用程序地址空间进行传输。

3.2 访问效率问题,标准IO为通用,只可以访问普通文件,但是效率比文件IO高;而文件IO是linux下专用,可以直接操作所有文件,不通过缓存,访问效率较低

3.3 文件I/O和标准I/O的本质区别:
        1)缓冲区:标准I/O函数接口在对文件进行操作时,首先操作缓存区,等待缓存区满足一定的条件时,然后再去执行系统调用,真正实现对文件的操作。而文件I/O不操作任何缓存区,直接执行系统调用。

        2)系统开销:使用标准I/O可以减少系统调用的次数,提高系统效率。例如,将数据写入文件中,每次写入一个字符。采用文件I/O的函数接口,每调用一次函数写入字符就会产生一次系统调用。而执行系统调用时,Linux必须从用户态切换到内核态,处理相应的请求,然后再返回到用户态,如果频繁地执行系统调用会增加系统的开销。

        3)执行效率:采用标准I/O的函数接口,每调用一次函数写入字符,并不着急将字符写入文件,而是放到缓存区保存,之后每一次写入字符都放到缓存区保存。直到缓存区满足刷新的条件(如写满)时,再一并将缓存区中的数据写入文件,执行一次系统调用完成此过程,这样便很大程度地减少了系统的调用次数,提高了执行效率。

如果有兴趣可以去验证两者的差别,用time 命令,例如:


 

 Real 是时钟时间-程序从开始至结束的总时间。他包括期间其他进程所占用的时间片和进程被阻塞的时间(如IO等待的时间)

 User 被测试程序在用户模式下所花的CPU时间。他是进程执行的正真的CPU时间。其他进程调度的时间片以及阻塞(如IO)的时间不包含在内。

Sys 是进程在内核中所花费的CPU时间。他表示进程在内核调用中所花的CPU时间,而程序的库调用仍然运行在用户空间下。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y_寒酥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值