C语言的文件操作
文件概述
- 标准输入输出
- 文件输入输出:处理大批量的数据,长久保存计算结果
- 文件时存储在外存(磁盘等)上的数据集合
- 每个文件通过唯一的文件名来表示,文件标示包括
- 文件路径
- 主文件名
- 文件后缀(拓展名)
- 计算机按文件名对文件进行读写等操作
- 文件按数据存储形式分类
- 文本文件(ASCII文件):将内存中的数据转换成ASCII码,每个字符用一个ASCII码存储
- 二进制文件:将内存中的数据按其内存中的存储形式不进行格式转换直接存放在文件上
- 文件存取方式
- 顺序存取:对文件进行打开,从头开始,顺序读写
- 随机存取:调用C语言库函数指定开始读写的字节号
- C语言文件操作步骤
- 对文件的处理通过调用标准的输入输出库函数来实现
- 文件操作一般采用**“缓冲文件系统”**的方式
- C语言文件处理的关键:定义一个文件指针,通过该指针对文件进行打开、读写、关闭操作。
FILE *文件指针标识符;
例如:FILR *fp1,*fp2;
- 使用FILE类型,需包含stdio.h文件
文件打开与关闭
文件打开
FILE *fopen(char *fname,char *mode); //按照mode规定的方式,打开由fname指定的文件
- fname:字符指针,指向要打开或建立的文件名字符串
- mode:字符指针,指向文件处理方式字符串
- 返回值:
- 正常返回:被打开文件的文件指针
- 异常返回:NULL,打开操作失败
- 文件名在程序运行中输入
FILE *fp;
char fname[5];
cout<<"Input filename:"
cin>>fname;
fp=fopen(fname,"r");
注:如果直接在程序中给出文件名应为fp=fopen("D:\\Doc\\ss.txt","a");
- 判断文件是否成功打开
if((fp=fopen(fname,mode))==NULL)
{
cout<<"can not open file";
exit(1);//文件打开失败,显示提示信息,并且调用exit()函数结束程序,该函数应该包含头文件"stdlib.h"
}
文件关闭
int fclose(FILE *fp); //关闭由fp所指的文件,释放由fp所指的文件类型结构体变量
- fp:一个已打开文件的文件指针
- 返回值:
- 正常返回:0
- 异常返回:EOF(-1),表示文件在关闭时发生错误
文件读写
- 文件提供字符读写、字符串读写、格式读写、数据块读写的顺序读写函数
【字符】
从文件读取字符
int fgetc(FILE *fp); //从fp所指文件中读取一个字符,并使文件指针后移一个字符位置
- fp:文件指针,指向要从中读取字符的文件
- 返回值:
- 正常返回:读取的字符
- 异常返回:EOF(-1)
- 返回函数说明:用于区分有效数据和输入的结束(EOF)
- 返回类型为char,一个字节无法区分EOF(0xFF)与有效数据(0xFF)
- 返回类型为int,四个字节区分EOF(0xFFFFFF)与有效数据(0x000000XX)
文件结束测试函数
int feof(FILE *fp); //判断文件是否结束
- fp:文件指针
- 返回值
- 0:假,表示文件未结束
- 1:真,表示文件结束。
- 实例
while(!feof(fp))
{
ch = fgetc(fp);
putchar(ch);
}
从文件写入字符
int fputc(int ch,FILE *fp); //把ch中的字符写入fp所指文件当前位置处,并使文件指针后移一个字符位置
- fp:文件指针,指向要从中读取字符的文件
- ch:整型变量,内存要写到文件中的字符
- 返回值:
- 正常返回:写入的字符
- 异常返回:EOF(-1)
问题:将文件file1.txt的内容显示到屏幕上,同时还将该文件中的数字字符复制到文件file2.txt中
#include<iostream>
using namspace std;
int main()
{
FILE *fp1,*fp2; //定义文件指针
char ch;//定义内存要写到文件中的字符(字符变量)
fp1=fopen("c:\\file1.txt","r");
if(fp1==NULL)
{ cout<<"不能打开文件";exit(1);
}
if((fp2=fopen("c:\\file2.txt","w"))==NULL)
{
cout<<"can not open file";
fclose(fp1);
exit(1);
}
while(!feof(fp1)) //从文件读取字符ch
{
ch = fgetc(fp1);
putchar(ch);//文件中的内容读到内存后显示在屏幕上
if(ch>='0'&&ch<='9') fputs(ch,fp2); //将数字字符复制在fp2中
}
fclose(fp1);
fclose(fp2);
system("pause");
return 0;
}
【字符串】
从文件读取字符串
char fgets(char *str,int n,FILE *fp); //从fp所指文件中读取n-1个字符,并把他们存在由str指出的字符数组中去,最后自动加上一个字符串结束符'\0'
- str:接受字符串的内存地址,可以是数组名或指针
- n:指出要读取字符的个数
- fp:文件指针,指向要从中读取字符的文件
- 返回值:
- 正常返回:字符串内存首地址,即str的值
- 异常返回:NULL
- 说明:
- 读入n-1个字符到文件,遇到换行符或者文件结束符提前结束
- 读入结束后,系统将自动在最后加’\0’
从文件写入字符串
int fputs(char *str,FILE *fp); 将str指向的字符串的内容输出到fp所指向文件的当前位置,同时将fp自动向前移strlen(str)个字符的位置
- str:可以是字符数组名、字符串常量或字符串指针
- fp:文件指针,指向字符串要写入其中的文件
- 返回值:
- 正常返回:非负整数
- 异常返回:EOF(-1)
- 说明:
- 字符串结束符’\0’不输出到文件
- 不自动在字符串末尾添加换行符
#include<iostream>
using namspace std;
int main()
{
FILE *fp; //定义文件指针
char s[100];//定义内存要写到文件中的字符串(字符串数组)
if((fp=fopen("c:\\file1.txt","w"))==NULL)
{
cout<<"can not open file";
exit(1);
}
fputs("I am a teacher",fp);
fputs("I love my students",fp);
fclose(fp); //必须先关闭,再以读的方式打开
if((fp=fopen("c:\\file1.txt","r"))==NULL)
{
cout<<"can not open file";
exit(1);
}
while(!feof(fp)) //从文件读取字符ch
{
fgets(s,16,fp);
puts(s);
}
fclose(fp);
system("pause");
return 0;
}
【数据块】 常用于二进制文件
从文件读取数据块
int fread(void *buffer,int size,int count,FILE *fp); //从文件指针fp所指的文件的当前位置读取字节数size大小的数据块共count个,存储到buffer所指的内存储区中
- butter:指向存放读入数据存储区的首地址指针。
- size:数据块的字节数,即一块数据块的大小
- count:一次读入数据块的数量
- fp:文件指针,指向要从中读取数据的文件
- 返回值:
- 正常返回:实际读取数据块的个数,即count
从文件写入数据块
int fwrite(void *buffer,int size,int count,FILE *fp); //将以buffer为起始地址的长度为size的count个数据块输出到文件指针fp所指的位置中去
- butter:指向存放读入数据存储区的首地址指针。
- size:数据块的字节数,即一块数据块的大小
- count:一次读入数据块的数量
- fp:文件指针,指向要从中读取数据的文件
- 返回值:
- 正常返回:实际输出数据块的个数,即count
常见的块读写应用示例:
float f;
double d[10];
fwrite(&f,sizeof(float),1,fp);//把浮点数f写入文件
fwrite(d,sizeof(double),10,fp);//将数组d中所有数写入文件
fread(&f,sizeof(float),1,fp);//把文件中以块形式读一浮点数到变量f中
fread(d,sizeof(d),1,fp);//从文件中一次性读一个数组d大小的数据块到数组d中
问题:从键盘上读入5个学生信息,将他们以文件的形式保存在磁盘上,然后再将从文件中读取学生信息,并在屏幕中显示出来。学生成绩信息包括学生姓名、学号、总分。
#include<iostream>
#define N 5
using namespace std;
struct student {
char name[20];
char no[7];
double score;
};
int main() {
int i;
struct student s[N], t[N];
FILE* fp;
if ((fp=fopen("student.dat","wb"))==NULL)
{
cout << "can not open student.dat";
exit(1);
}
for (i = 0; i < N; i++)
{
cin >> s[i].name >> s[i].no >> s[i].score;
fwrite(&s[i],sizeof(student),1,fp);//逐元素向文件写入数据
}
fclose(fp);//写文件结束先关闭,再以读的方式打开
if ((fp = fopen("student.dat", "rb")) == NULL)
{
cout << "can not open student.dat";
exit(1);
}
fread(t, sizeof(t), 1, fp);//一次从文件中读出整个数组
for (i = 0; i < N; i++)
{
cout << t[i].name << ' ' << t[i].no << ' ' << t[i].score << endl;
}
fclose(fp);
system("pause");
return 0;
}
【文件格式化】
从文件格式化读取数据
int fscanf(FILE *fp,const char *format,add_list); //按照format指出的格式,从fp指定的文件中读取数据存放至地址表列(add_list)的变量中
- format:指向读取数据格式字符串的指针
- add_list:存放读取数据的变量的地址表列
- fp:文件指针,指向要从中读取数据的文件
- 返回值:
- 正常返回:成功读取参数的个数
- 异常返回:EOF(-1)
- 示例:
fscanf(fp,"%d%d","&x&y");
表示从fp所指的文件中顺序读取两个整数给变量x和y
向文件格式化写入数据
- int fprintf(FILE *fp,const char *format,add_list); //将变量表列(add_list)中的数据,按照format指出的格式,写入由fp指定的文件`
- format:指向写出数据格式字符串的指针
- add_list:要写入文件的变量表列,各个变量之间用逗号分隔
- fp:文件指针,指向将数据写入的文件
- 返回值:
- 正常返回:输出的字节数
- 异常返回:负值
- 示例:
fprintf(fp,"%d%s",4,"China");
表示将整数4和字符串“China”写入fp所指的文件中。
问题:用格式化读写文件方式实现上述相同的功能
#include<iostream>
#define N 2
using namespace std;
int main() {
char name[20], num[80];
double score;
int i;
FILE* fp;
if ((fp=fopen("student","wb"))==NULL)
{
cout << "can not open student";
exit(1);
}
for (i = 0; i < N; i++)
{
cin >> name >> num >> score;
fprintf(fp, "%s %s %1f", name, num, score);
}
fclose(fp);
for ( i = 0; i < N; i++)
{
fscanf(fp, "%s%s%1f", name, num, &score);
cout << name << " " << num << " " << score << endl;
}
fclose(fp);
system("pause");
return 0;
}
随机读写
- 顺序读写文件只能从头开始,顺序读写各个数据
- 随机读写可按照需要只读写文件中某些指定的部分
- 随机读写的关键是要按要求移动位置指针,即进行文件定位
- 实现文件定位、移动文件内部位置指针的函数主要有rewind函数和fseek函数
【rewind】
函数原型:void rewind(FILE *fp);
功能说明:使得指示文件位置的指针重新返回到文件开始
【fseek】
- 函数原型:
int fseek(FILE *fp,long offset,int whence);
- 功能说明:使得文件指针fp移动到基于whence的相对位置offset处
- 参数说明:
1.offset是相对于whence的字节位移量,用长整型表示。
2.whence是移动的基准,常用符号常量表示 - 返回值:
1.正常返回:0
2.返回出错:-1 - 参数whence的意义
- 示例
//把读写位置从当前位置向后移动1234字节(L后缀表示长整数)
fseek(fp,1234L,SEEK_CUR);
//把读写位置移动到文件末尾
fseek(fp,0L,2);