分类:
2012-04-12 23:56:06
原文地址:Linux下文件I/O操作详解 作者:fly123456789
1. 文件I/O操作类型
文件在Linux系统中是一个广泛的概念,Linux将所有的硬件设备当作文件来处理。文件的I/O分为两种类型,第一种类型是非缓冲式文件操作,主要是由系统调用提供,另一种是缓冲式I/O操作,主要是由C语言的标准输入输出库函数提供。
2. 非缓冲式文件I/O操作
非缓冲式文件操作对于小规模文件的读写,或者是实时设备,执行非缓冲式文件操作,应用程序能够立即得到数据。非缓冲式文件操作的函数主要是由系统调用提供,有以下几个函数,read(),write(),lseek().
文件标识符是一个整数,系统预留了3个文件标识符:
0 代表标准输入,即通过键盘输入
1 代表标准输出,即输出到终端
2 标准错误,系统中存放错误信息的堆栈
read(文件标识符,内存指针,内存块长度)
作用: 从文件中把数据读入到内存块中。
返回值: read函数的返回值是读取数据的长度。如果返回0,表示没有读任何数据,如果返回1,表示运行时错误。
write(文件标识符,内存指针,内存块长度)
作用: 将指定长度的数据写入到文件中去。
返回值: write函数返回j是实际写入文件中的数据长度。如果返回0,表示没有写任何数据,如果返回1,表示运行时错误。
lseek(文件标识符,偏移长度,起始位置)
作用: 用来设置读写指针的位置
起始位置有三个宏: SEEK_SET,SEEK_CUR,SEEK_END,分别是以文件开始位置,当前位置,文件尾为参考位置进行偏移。
非缓冲式文件操作程序:
用到的函数有open,read,write,close,这些都是系统调用提供的函数。
/**
文件的输入输出操作是对设备进行操作的基础
第一部分是非缓冲性文件操作,这种操作适合于小规模文件的读写和对实时性要求比较高的文件的读写,这类操作是系统调用提供的
第二部分是缓冲性文件操作,所面向的则是大规模非实时性数据处理问题,这类操作是标准输入输出库提供的
非缓冲性文件操作函数只有两个,read()和write()函数,这些函数通过文件标识符找到文件
有3个系统预定义的文件标识符
0标准输入,即通过终端输入
1标准输出,即通过终端输出
2标准错误 ,系统存放错误信息的堆栈
read(文件标识符,内存块指针,内存块长度);//从文件中读取数据保存到内存块中,返回实际读取的长度,如果返回值是0,表明没有读取任何数据,运行错误时返回1
write(文件标识符,内存块指针,内存块长度);//将指定的数据写入到文件中去,返回实际写入的长度,返回值为0,表明没有写入任何数据,运行错误时返回1
lseek(文件标识符,偏移长度,起始位置);//用来指定读写指针的具体位置 SEEK_SET以开始 位置为参考坐标,将指针设置到偏移位置上, SEEK_CUR以当前位置为参考位置,将指针设置到偏移位置上 SEEK_END 以结束位置为参考位置,将指针设置到偏移位置上,SEEK_CUR,SEEK_END时,指针偏移位置可以是负值,表示向前偏移
**/
#include
#include
#include
#include
#include
#include
#include
#include
#define LENGTH 20000
void read1(const char* f_path);
void write1(const char* fsrc_path,const char* fdes_path);
int main(){
const char* fsrc_path="/etc/passwd";
const char* fdes_path="test.txt";
read1(fsrc_path);//读取内容输出到终端
write1(fsrc_path,fdes_path);//将读取的内容保存到指定的文件中去
read1(fdes_path);//再将写入文件显示到终端上
return 0;
}
void read1(const char* f_path){//读取文件的内容到内存块上,并输出显示到终端上
int real=0;//定义实际读取的字节数
int i=0;
int f;//定义文件标识符
char c[LENGTH];
f=open(f_path,O_RDONLY);//以只读的方式读文件
if(f!=-1){
real=read(f,c,LENGTH);//第一个参数是文件描述符,第二个参数是内存块指针,第三个参数是内存块长度
if(real>0){
for(;real>0;real--){
printf("%c",c[i++]);//打印到终端
}
}
else{
perror("读取错误");
}
}
else {
perror("文件打开错误");
}
}
void write1(const char* fsrc_path,const char* fdes_path){//将从文件fsrc_path读出的内容写入到文件fdes_path中去
int fdes;//目标文件的文件描述符
int fsrc;//源文件的文件描述符
int realread=0;//实际从文件读取的字节数
int realwrite=0;//实际写入到文件的字节数
int i=0;
char c[LENGTH];//定义内存块指针
fdes=open(fdes_path,O_RDWR);
if(fdes==-1){
fdes=open(fdes_path,O_RDWR|O_CREAT,0664);//如果目标文件不存在,则建立一个目标文件
}
fsrc=open(fsrc_path,O_RDONLY);
if(fsrc!=-1){
realread=read(fsrc,c,LENGTH);
if(realread>0){
write(fdes,"123",3);
lseek(fdes,0,SEEK_CUR);//将指针移动到文件的第2个字符上进行写
realwrite=write(fdes,c,realread);
if(realwrite==realread){
printf("%s/n","写入文件成功");
}
else{
perror("写入失败");
}
close(fdes);
close(fsrc);
}
else{
perror("读取错误");
}
}
else{
perror("文件打开错误");
}
}
read1()函数的功能是读取文件的内容显示到终端。
write1()将从源文件中读取的内容写到目的文件中去,可以实现文件的复制。
3. 缓冲式文件I/O操作
(1) 缓冲区是为程序分配的内存块,在进行数据比较大且不要求实时性的I/O操作时,一部分数据被放于缓冲区中,只有当数据块的长度要超过缓冲区的长度或时间周期到达时,才把这些数据送到指定的位置。基于缓冲区的设备操作是为了减少读写的次数,从而减小系统开销,主要适用于大量数据的读写操作。
这部分函数是由标准输入输出提供的,即stdio. 用FILE*结构体指针作为文件流指针标识,同时系统预定义了3个文件流指针,分别是:
stdin 标准输入
stdout 标准输出
dtderr 标准错误
注意: 文件标识符是文件流指针FILE结构体中的一项成员变量。
主要的函数有: fopen,fclose,fread,fwrite,fflush,fscanf,fprintf,fgetpos,fsetpos,ftell,rewind,fgets,fputs
FILE* fopen(路径,打开方式)
参数: 打开方式有: r,w,rw+
作用: 打开一个文件,返回文件流指针,如果打开失败,则返回文件流指针为NULL.
fread(缓冲区指针,长度,数量,文件流指针)
参数: 第一个参数是缓冲区指针,第二个参数是每次读取文件的长度,第三个参数是最多读取的次数,最后一个参数是文件流指针
作用:从文件中读取数据放到缓冲区中去。
返回值: 实际读取到缓冲区内的次数。
fwrite(缓冲区指针,长度,数量,文件流指针)
参数:fread
作用: 将缓冲区内的数据写入到文件中去
返回值:实际写入文件的次数
fflush(文件流指针)
作用: 如果待写入的数据存在于缓冲区中,又想把数据立刻写到文件中去,则调用fflush.
fclose(文件流指针)
作用: 关闭文件流
程序1: 将一个文件中的数据写入到另外一个文件中去
/**
fread,fwrite,fopen,fclose函数
size_t fread(缓冲区指针,长度,数量 ,文件流指针);//返回值是一个整形,为实际读入缓冲区的数量,即读取到缓冲区的次数 第一个参数是缓冲区指针,第二个参数是每次读到缓冲区的数据的长度,第三个参数是读操作的次数,第四个参数是文件流指针,fopen返回一个文件流指针,作为文件的标识,如果实际文件的长度大于fread()函数实际读取的数据长度,那么fread()实际读取的长度为第三个参数数量与长度的乘积
size_t fwrite(缓冲区指针,长度,数量,文件流指针);//把缓冲区的数据写入到文件中去 第一个参数是缓冲区指针,第二个参数是每次写入文件的缓冲区的长度,第三个参数是写文件的次数,第四个参数是文件流指针,这个函数的返回值是实际写文件的次数
形式同 fread函数
fopen(路径,打开方式);//返回一个文件流指针 FILE* fp,如果fp为NULL,打开文件失败,但是fopen不能建立文件
fclose(FILE* fp)//关闭文件,如果返回值为-1,关闭文件失败
fflush(FILE*fp);//将缓冲区内的数据立即写到文件中去
其实在fclose(FILE*fp);执行时,把缓冲区内的数据写入到文件中去,隐含执行了一次fflush(FILE*fp)
**/
//程序的功能是从一个文件读取数据写入到另外一个文件中去,利用缓冲区读写
#include
#define LENGTH 1024
#define SIZE 65536
//上面两个参数分别是每次读缓冲区或者是写缓冲区的长度,SIZE定义的为缓冲区的大小,即每次读写1KB的数据,缓冲区大小为64KB
void fread1(const char* src_path);//把数据读到缓冲区char中
void fwrite1(const char* des_path);//再把数据写到上的文件中去
char buffer[SIZE];
int main(){
const char* src_path="/etc/esd.conf";
const char* des_path="test";
fread1(src_path);
fwrite1(des_path);
return 0;
}
//读文件到缓冲区中
void fread1(const char* src_path){
FILE* fp;//定义一个文件流指针
int count=0;//实际读文件的次数
fp=fopen(src_path,"r");
if(fp!=NULL){
count=fread(buffer,LENGTH,SIZE/LENGTH,fp);
if(count>=0){
printf("%s/n","文件成功读到缓冲区中");
}
else{
perror("读取文件失败");
}
}
else{
perror("源文件打开");
return ;
}
fclose(fp);//关闭文件流
}
//把数据从缓冲区内写入到文件中去
void fwrite1(const char* des_path){
FILE* fp;
fp=fopen(des_path,"rw+");//以读写方式打开
int writecount=0;//定义从缓冲区写入文件的实际次数
if(fp!=NULL){
writecount=fwrite(buffer,LENGTH,SIZE/LENGTH,fp);
fflush(fp);//将缓冲区内的数据立即写到文件中去
if(writecount>=0){
printf("%s/n","从缓冲区向文件写入成功");
}
else{
perror("从缓冲区向文件写入失败");
return;
}
}
else{
perror("文件打开失败");
return;
}
if(fclose(fp)==-1){
perror("关闭文件流失败");
return;
}
else{
printf("%s/n","关闭文件流成功");
}
}
(2)下面的两个函数主要用于文件流的格式化输入与输出
fscanf(文件流指针,""控制字符串",输入项列表)
fprintf(文件流指针,"控制字符串","输出项列表)
fscanf作用: 从文件流中读取数据保存到输入项列表中去。
fprintf作用: 将格式化后的数据输出到文件中去。
fprintf与printf区别: printf是向终端输出,而printf是向文件中输出,即重定向。
fscanf与scanf区别: scanf是从键盘输入,即标准输入而fscanf是从文件中输入,重定向了。
程序2: 文件流格式化输入与输出程序
/**
fprintf与fscanf函数的应用
本程序完成的功能是文件流的格式化输入与输出
fprintf(文件流指针,“控制字符串”,输入输出列表);//向文件中输出数据,即写数据到文件中,返回值是实际写到文件中的数据长度
fscanf(文件流指针,"控制字符串",输入输出列表);//读文件中的数据,把文件中的数据格式化读入结构体或内存中 ,返回值是正确输入项的个数
fprintf是向文件写数据,fscanf是读文件数据,这两个函数是相对文件而言的,其中fprintf函数的控制字符串一定要用空格格开 %s %s有空格
fscanf(stdin,"%d%d",&a,&b); 相当于scanf("%d%d",&a,&b); stdin是标准的输入函数,把数据保存到a,b所指向的地址中去
fprintf(stdout,"%d %d",a,b); 相当于printf("%d %d",a,b); stdout是标准的输出函数,把数据标准输出
fprintf与printf区别: printf是向标准输出,即向控制台输出,而fprintf是向文件输出,即重定向
fscanf与scanf区别: scanf是标准输入,即从键盘输入,而fscanf是从文件输入
sprintf是用来格式化字符串,常用来把整数格式化成字符串放到字符数组中去
**/
#include
#include
#include
#include
int main(){
//首先向文件把数据写到文件中去,然后再把写到文件中的数据格式化输出到结构体中去
struct buddy{
char name[50];
int tel;
char address[200];
}bd1;
if(open("buddy",O_CREAT|O_RDWR,0664)==-1){
perror("文件创建失败");//open是系统调用提供的函数
return 0;
}
FILE* fp;//定义一个文件流指针,打开文件
fp=fopen("buddy","rw+");//fopen是标准C输入输出库函数
if(fp==NULL){
perror("打开文件失败");
return 0;
}
//把数据格式化输出到文件中
int realwrite=fprintf(fp,"%s %d %s","TOM",12333,"CHINA");
if(realwrite<=0){
perror("数据格式化输出到文件失败");
return;
}
fclose(fp);//当把数据写入到文件中时,隐藏指针已经到了文件尾部,此时需要关闭文件流指针,当重新打开文件时,隐藏指针又回到了文件的并首部
fp=fopen("buddy","rw+");
fscanf(fp,"%s%d%s ",bd1.name,&bd1.tel,bd1.address);//把文件中的数据格式化读入到结构体中去
fclose(fp);
printf("%s%d%s/n",bd1.name,bd1.tel,bd1.address);//显示结构体中的数据
return 0;
}
(3)以下的几个函数是用于文件流的定位操作
fgetpos(文件流指针, fpos_t *位置)
参数: fpos_t类型是标准函数库中定义的一种结构体,它也是文件流指针FILE结构体中的一个成员。
作用: 获得当前文件的读写位置
fsetpos(文件流指针, const fpos_t *位置)
作用: 设置当前文件的读写位置
ftell(文件流指针)
作用: 获得文件当前读写的位置偏移量
rewind(文件流指针)
作用: 将文件指针重新指向文件流开头
程序3: 文件定位功能
/**
完成标准输入输出库中的文件定位功能
fgetpos(文件流指针,fpos_t* 位置);//获取当前文件的读写位置,并将位置存放到fpos_t指针中去
fsetpos(文件流指针,const fpos_t*位置);//通过fpos_t设置当前读写的位置
ftell(文件流指针);//获得当前读写的位置偏移量
rewind(文件流指针);//将文件指针重新指向文件流的开头
ferror(文件流指针);//判断文件流操作是否失败,失败返回非0,否则返回0
feof(文件流指针);//判断文件流指针是否到达文件尾,到达文件尾返回非0,否则返回0
**/
#include
#include
#include
#include
#include
#include
#include
#define CTIME 3
int main(){
struct inputvalue {
int length;//输出到文件中数据的长度
fpos_t pos;//输出到文件中数据的位置
};
struct inputvalue iv[CTIME];//定义一个结构体指针
int f=open("text",O_CREAT|O_RDWR,0664);//创建一个文件,返回文件流指针
if(f==-1){
perror("创建文件失败");
return 0;
}
//用fprintf格式化向文件中输入数据
//打开文件流指针
FILE* fp;
fp=fopen("text","rw+");//以读写的方式打开文件流指针
if(fp==NULL){
perror("打开文件流失败");
return 0;
}
int i;
for(i=0;i
int realwrite=fprintf(fp,"第%d次写入的数据",i);
if(realwrite>0){//实际输出数据的长度
iv[i].length=realwrite;//把实际输出文件的长度保存到结构体的length变量中去
}
}
printf("文件流的总长度为%d/n",ftell(fp));//此时指针已经到了文件流尾,所以获得的是文件流的总长度
fflush(fp);
//fclose(fp);//关闭文件流指针
rewind(fp);//将指针重新指向文件头部
//用read()函数把保存到文件中的数据进行输出
for(i=0;i
*(buf+iv[i].length+1)='/0';
fsetpos(fp,&iv[i].pos);//将文件流指针放到当前位置
fread(buf,iv[i].length,1,fp);
printf("%s/n",buf);
free(buf);//释放空间
}
fclose(fp);//关闭文件流指针
fp=fopen("text","rw+");//以读写的方式打开文件流指针
if(fp==NULL){
perror("打开文件流失败");
printf("错误代码:%d",ferror(fp));
return 0;
}
char* p=malloc(100);
while(!feof(fp)){//到达尾返回非0,没到达尾返回0
fgets(p,100,fp);//读取字符串到字符指针指向的位置
}
printf("%s",p);
free(p);
fclose(fp);//关闭文件流指针
return 0;
}
(4) 文件流操作的其它函数
fgets(字符串指针,最大长度,文件流指针)
作用: 从文件读取一个字符串保存到字符串指针指向的位置,操作成功后返回字符串指针
fputs(字符串指针,文件流指针)
作用: 向文件输出一个字符串,操作成功后返回字符串的长度
错误处理函数:
ferror(文件流指针)
作用: 判断文件流操作是否失败,失败时返回非0值,否则返回0.
feof(文件流指针)
作用: 判断是否到达文件尾,到尾返回非0,没到尾返回0
clearee(文件流指针)
作用: 清除error变量内的错误信息
总结: Linux下的文件I/O操作分为非缓冲式操作与缓冲式操作,非缓冲式操作的函数主要是由系统调用提供的,缓冲式I/O操作主要是由C语言的标准输入输出库来提供的。非缓冲式适用于小文件的读写,实时性强,而非缓冲式I/O操作适用于非实时性任务,大量数据的读写,能够减小系统开销。