fopen,open,mmap函数在Linux文件编程中的应用总结

    fopen是C语言库函数,open是系统调用,mmap是将大文件映射到内存中使用。fopen与open函数区别网上有很多的介绍,但是大多数都是没有注意平台上的差异(特别是百度百科)。在Linux文件编程中,fopen函数的使用就有不少的坑,如果照搬百度上的定义,则有可能会出现异常的问题。


1.fopen函数

    一般与fopen同时使用的还有fwrite,fread,fclose。看下面的代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define	FILE_NAME	"./TestFile"

struct student{
	char name[20];
	short age;
	float score;
	char sex;
};

/* 写内存到文件 */
int WriteBuffToFile()
{
	int i = 0;
	FILE* fd = NULL;
	struct student stu[5];

	fd = fopen(FILE_NAME,"w+");
	if(NULL == fd)
	{
		printf("open error \n");
		return -1;
	}
	printf("open ok\n");
	memset(stu,0,sizeof(stu));	
	for(i=0;i<5;i++)
	{
		memcpy(stu[i].name,"biao",strlen("biao")+1);
		stu[i].age=i;
		stu[i].score=89.12f;
		stu[i].sex='m';
		fwrite(&stu[i], sizeof(stu[i]), 1, fd);
	}
	fclose(fd);
	return 0;
}

int ReadFileToBuff()
{
	int i = 0;
	int ret = 0;
	FILE* fd = NULL;
	struct student stu[5];
	
	fd = fopen(FILE_NAME,"r+");
	if(NULL == fd)
	{
		printf("open error \n");
		return -1;
	}

	printf("open ok\n");
	//memset(stu,0,sizeof(stu));
	for(i=0;i<5;i++)
	{  
		fread(&stu[i], sizeof(stu[i]), 1, fd);
    }  
    fclose(fd);
    for(i=0;i<5;i++)
	{  
        printf("stu[%d].name=%s\n",i,stu[i].name);  
        printf("stu[%d].age=%d\n",i,stu[i].age);  
        printf("stu[%d].sex=%c\n",i,stu[i].sex);  
        printf("stu[%d].score=%f\n",i,stu[i].score);          
    }   
    return 0;  
}

int main()
{
	int ret = 0;
	if(0 == WriteBuffToFile())
	{
		ReadFileToBuff();
	}
	return 0;
}

我们看fopen的参数:百度百科上的介绍是:

    r以只读方式打开文件,该文件必须存在。
    r+ 以读/写方式打开文件,该文件必须存在。
    rb+ 以读/写方式打开一个二进制文件,只允许读/写数据。
    rt+ 以读/写方式打开一个文本文件,允许读和写。
    w 打开只写文件,若文件存在则长度清为 0,即该文件内容消失,若不存在则创建该文件。
    w+ 打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
    a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留(EOF 符保留)。
    a+ 以附加方式打开可读/写的文件。若文件不存在,则会建立该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的 EOF 符不保留)。
    wb 以只写方式打开或新建一个二进制文件,只允许写数据。
    wb+ 以读/写方式打开或建立一个二进制文件,允许读和写。
    wt+ 以读/写方式打开或建立一个文本文件,允许读写。
    at+ 以读/写方式打开一个文本文件,允许读或在文本末追加数据。
    ab+ 以读/写方式打开一个二进制文件,允许读或在文件末追加数据。

    如果我们按照上面的参数设置open 模式为“wb+”, 正确的执行应该是:文件可读可写,如果文件存在则打开文件,如果文件不存在则创建文件。在我注释掉第55行代码的时候,可以得到正确的输出。如果不注释掉该句就会发现,读函数打开文件的时候,它实际上已经将旧的文件删除,然后重新创建了一个新的文件。至于为什么会这样,我也不太清楚,但从man fopen 中可以得到解释:
The argument mode points to a string beginning with one of  the  following  sequences  (possibly
       followed by additional characters, as described below):
       r      Open text file for reading.  The stream is positioned at the beginning of the file.
       r+     Open for reading and writing.  The stream is positioned at the beginning of the file.
       w      Truncate  file  to zero length or create text file for writing.  The stream is positioned
              at the beginning of the file.
       w+     Open for reading and writing.  The file is created if it does not exist, otherwise it  is
              truncated.  The stream is positioned at the beginning of the file.
       a      Open  for  appending (writing at end of file).  The file is created if it does not exist.
              The stream is positioned at the end of the file.
       a+     Open for reading and appending (writing at end of file).  The file is created if it  does
              not  exist.   The  initial file position for reading is at the beginning of the file, but
              output is always appended to the end of the file.

在linux 中fopen的参数很简单明了,它并不区分二进制文件和文本文件。所以当我们在Linux中使用fopen的时候需要特别的注意打开的模式

2.open函数
    open 函数在Linux中使用得非常频繁,属于系统调用,一起的还有close,read,write 等,下面是个简单的应用实例:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#define	FILE_NAME	"./TestFile"

struct student{
	char name[20];
	short age;
	float score;
	char sex;
};

/* 写内存到文件 */
int WriteBuffToFile()
{
	int i = 0;
	int fd = -1;
	struct student stu[5];
  	mode_t mode;
	
	/*设置建立新文件时的权限掩码 */
	mode = umask(000); 
	fd = open(FILE_NAME,O_RDWR|O_CREAT|O_EXCL,00666);
	if(fd <= 0)
	{
		printf("open:%m\n");
		umask(mode);
		return -1;
	}
	printf("open ok\n");
	memset(stu,0,sizeof(stu));	
	for(i=0;i<5;i++)
	{
		memcpy(stu[i].name,"biao",strlen("biao")+1);
		stu[i].age=i;
		stu[i].score=89.12f;
		stu[i].sex='m';
		write(fd,&stu[i],sizeof(stu[i]));		
	}
	close(fd);
	umask(mode);
	return 0;
}
/* 读文件到内存 */
int ReadFileToBuff()
{
	int i = 0;
	int fd = -1;
	struct student stu[5];
  	mode_t mode;
	
	/*设置建立新文件时的权限掩码 */
	mode = umask(000); 
	fd = open(FILE_NAME,O_RDWR,00666);
	if(fd <= 0)
	{
		printf("open:%m\n");
		umask(mode);
		return -1;
	} 
    printf("open ok! can read;\n");  

	memset(stu,0,sizeof(stu));	
    for(i=0;i<5;i++)
	{  
        read(fd,&stu[i],sizeof(stu[i]));  
    }  
    close(fd);
	
    i=0;  
    for(;i<5;i++)
	{  
        printf("stu[%d].name=%s\n",i,stu[i].name);  
        printf("stu[%d].age=%d\n",i,stu[i].age);  
        printf("stu[%d].sex=%c\n",i,stu[i].sex);  
        printf("stu[%d].score=%f\n",i,stu[i].score);          
    }  
    umask(mode);  
    return 0;  
}

int main()
{
	int ret = 0;
	if(0 == WriteBuffToFile())
	{
		ReadFileToBuff();
	}
	return 0;
}
3.mmap函数
    在遇到大文件的时候,比如要对几百M或是几G 的文件进行编辑,直接将他们读取到内存中来编辑是不现实的,应为用户的堆栈空间是有限的,根本不可能这样来操作。这种时候就需要内存映射了。将文件映射到内存,它只是开辟了一块空间用来处理这个文件,实际上这块空间的大小并不是文件的大小,它只是开辟了一块空间来做数据交换。
    使用mmap映射文件,这样可以向操作内存一样来操作文件,而且效率高;但是有一定的限制, 
        · 文件的长度必须大于等于映射的长度; 
        · 映射的offset必须是页(page)的整数倍;

特别需要注意映射不会增长文件的长度;映射部分的内容应该是文件本来就应该有的内容。比如你创建一个新文件,该文件为空,如果你把它映射到内存空间,通过内存操作你是无法将数据写入到文件去的。

下面是内存映射文件的一个实例代码

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <sys/mman.h>  
#include <sys/stat.h>  

#define	FILE_NAME	"./TestFile"

typedef struct{  
  char name[20];  
  short age;  
  float score;  
  char sex;  
}student;  

int main()  
{  
	int fd,i,ret,len; 
	struct stat st;   
	student *p; 
	
	fd = -1; i=0; ret = 0; len = 0;
    fd=open(FILE_NAME,O_RDWR);  
    if(fd==-1)
	{	
        printf("打开文件失败:%m\n");  
        exit(-1);  
    }  
	printf("open ok!\n");    
		
	/*获取文件信息*/  
	ret = fstat(fd,&st);  
	if(ret == -1)
	{  
		printf("获取文件大小失败:%m\n");  
		close(fd);  
		exit(-1);  
	}  
	len=st.st_size;  
    
	/*把文件映射成虚拟内存地址*/  
	p=mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);      
	if(p==NULL || p==(void*)-1)
	{  
		printf("映射失败:%m\n");  
		close(fd);  
		exit(-1);  
	}  
 
	/*通过内存读取记录*/  
	while(i<(len/sizeof(student)))  
	{  
		printf("第%d个条\n",i);  
		printf("name=%s\n",p[i].name);  
		printf("age=%d\n",p[i].age);  
		printf("score=%f\n",p[i].score);  
		printf("sex=%c\n",p[i].sex);  
		i++;  
	}    
	/*卸载映射*/  
	munmap(p,len);  
	/*关闭文件*/      
	close(fd);      
}  

    最后总结一句话:在Linux编程中,无论是系统或是库函数,如有有不太清楚的,建议直接找男人(linux 中man 命令),它比百度谷歌得到的描述更加的准确。本人就被百度坑过不少回  =_=

阅读更多

扫码向博主提问

li_wen01

非学,无以致疑;非问,无以广识
去开通我的Chat快问
版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/li_wen01/article/details/78535421
个人分类: Linux 文件编程
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭