文件的输入输出(C/C++)
一、I/O流的类的层次结构
二、文件概述
1、文件的分类
①文本文件:一种由若干行字符构成的计算机文件,其编码为ASCII码UNICODE码等等,可以用文本编辑器编辑。
②二进制文件:除上述文本文件以外的其他文件(视频,音频,图片等等)
三、文件的输入输出 (C )
1.文件类型的指针
每个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息(如文件名、文件状态及文件当前位置等)。这些信息保存在一个FILE类型的结构体变量中。
若 FILE *fp; 则fp就称为指向文件类型的指针变量。访问文件通过文件指针进行
FILE类型的结构体定义如下:
typedef struct{
short level; //缓冲区“满”或“空”的程度
unsigned flags; //文件状态标志
char fd; //文件描述符
unsigned char hold; //如无缓冲区不读取字符
short bsize; //缓冲区的大小
unsigned char *buffer; //缓冲区的位置
unsigned char *curp; //当前读写指针
unsigned istemp; //临时文件,指示器
short token; //用于有效性检验
} FILE;
2.文件操作函数
C中文件操作函数包含在stdio.h中
#include<stdio.h>
如果你和我一样用的是Visual Studio 2019,在使用下面介绍的函数时,编译器可能会发出警告,关闭警告的方法:
1.打开项目的 “属性页” 对话框。 有关如何使用 “属性页” 对话框的信息。
2.选择 “配置属性” > CC++ / > “高级” 页。
3.编辑 “禁用特定警告” 属性以添加 4996。 选择 “确定” 以应用所做的更改。
文件的打开(fopen函数)
函数原型:FILE *fopen(char *name,char *mode);
说明:
——调用方式:fopen(“文件名”,“文件模式”)
——函数返回值:正常打开,返回指向文件结构体的指针;打开失败,返回 NULL
——文件名:如:“e:\\c++\\file.txt” ("\\"也可以用“/”代替)在e盘c++文件下打开名为“file”的txt类型的文件(文本文件)。若缺少文件路径,默认为当前所在目录(就是说,如果你在当前目录打开一个名为“file”的txt文件,可以不用写上文件路径:“file.txt”)。
——文件模式:
r:打开一个文本文件只读(若文件不存在,则打开失败)
w:打开一个文本文件只写(若已经有此文件,则将其原有内容清空,若文件不存在,则建新文件)
a:打开一个文本文件只写 (若已经有此文件,写入的数据追加到文件末尾,若文件不存在,则建新文件)
rb:打开一个二进制文件只读
wb:打开一个二进制文件只写
ab:对一个二进制文件添加
r+:打开一个文本文件读/写(若文件不存在,则打开失败)
w+:打开一个文本文件读/写(若已经有此文件,则将其原有内容清空,若文件不存在,则建新文件)
a+:打开一个文本文件读/写 (若已经有此文件,写入的数据追加到文件末尾,若文件不存在,则建新文件)
rb+:打开二进制文件读/写
wb+:打开二进制文件读/写
ab+:打开二进制文件读/写
在进行文件操作时,先要定义一个文件指针
例:
FILE *fp;
fp=fopen("d:\\user\\myfile.txt","r+");
文件的关闭(fclose函数)
函数原型:int fclose(FILE *fp);
说明:
——调用方式:fclose(文件指针)
——返回值:如果文件顺利关闭,该值为0,否则为-1
——作用:断开使文件指针与文件的关联,否则该文件指针关联的文件不能够被其他文件指针访问,该文件指针也不能关联到其他文件。
例:
fclose(fp);
文件的读写
(1) 文本文件的读写:
读写的对象:文本文件
①fscanf函数
函数原型:int fscanf(FILE * fp,char const *const,…);
说明:类似于scanf函数。
例:
int a;
fscanf(fp,"%d",&a);
②fprintf函数
函数原型:int fprintf(FILE * fp,char const *const,…);
说明:类似于fprintf函数。
int b;
fscanf(fp,"%d",b);
③fgetc函数
函数原型:int fgetc(FILE *fp);
说明:
——功能:读取文件中的一个字符(文件的位置指针处的字符,位置指针在“3、文件的定位”中有说明)
例:
——返回值:读取成功返回所读取字符的ASCII值,读取失败返回EOF(-1)
//文件指针fp打开文件的文件模式可进行读操作
char ch;
ch=fgetc(fp)
④fputc函数
函数原型:int fputc( int Character,FILE* fp);
说明:
——功能:将一个字符写入文件
——Character:需要写入的字符
——fp:文件指针
——返回值:写入成功返回Character的ASCII值(十进制),写入失败返回EOF("stdio.h"中定义的一个宏,#define EOF (-1))
例:
//文件指针fp打开文件的文件模式可进行写操作
char ch='a';
fputc(ch,fp);
⑤fgets函数
函数原型:char *fgets(char *Buffer,int MaxCount,FILE *fp);
说明:
——功能:读取文件中的一串字符
——Buffer:一个字符数组,将从文件中读取的字符放到其中。
——MaxCount:要读取字符的数目(注意:读取得到的字符串会自动在字符串末尾加上结束符’\0’,由于结束符也是要算入字符串长度的,所以实际最多只会从文件中读取MaxCount-1个字符。为什么说是最多?因为如果在读取到 MaxCount-1 个字符之前如果出现了换行符(会将换行符也读入到Buffer),或者读到了文件末尾,则读取结束。这就意味着,不管 n 的值多大,fgets() 最多只能读取一行数据,不能跨行。)
——fp:文件指针
——返回值:读取成功返回Buffer,即字符数组的首地址,读取失败返回nullptr(空指针)
例:
#include<stdio.h>
int main(){
FILE *fp;
fp=fopen("myf.txt","r");
if(!fp){
exit(0);
}
char str1[20];
fgets(str1,100,fp);
printf("%s",str1);
char str2[20];
printf("%s",fgets(str2,100,fp));
}
如果在读取文本文件中的汉字时,所读取的内容为乱码,在代码没有错误的情况下,这可能是因为文本文件本身的问题,但是具体解决方法我也不知道,望大神告知。
⑥fputs函数
函数原型:int fputs(char const* Buffer,FILE* fp);
说明:
——功能:将一串字符写入文件中
——Buffer:要写入到文件的字符串
——fp:文件指针
——返回值:写入成功返回0,若写入失败返回EOF(-1)
例:
//文件指针fp打开文件的文件模式可进行写操作
char str[12]="会好起来的";
fputs(str,fp);
(2)二进制文件的读写
读写对象:二进制文件
①fread函数
函数原型:size_t fread(void* Buffer, size_t ElementSize,size_t ElementCount, FILE* fp);
说明:
——功能:以二进制方式存放的文件输入到程序的数据结构中
——Buffer:一个泛型指针,可以接受任意类型的指针
——ElementSize:读入数据每一项的长度
——ElementCount:需要读入数据的项数
——fp:文件指针
例:
#include<stdio.h>
int main(){
FILE *fp;
fp=fopen("file.dat","rb");
if(!fp){
exit(0);
}
int arr[10];
fread(arr+2,sizeof(int),3,fp); //将从二进制文件中读出的数据依次放到arr[2],arr[3],arr[4]
fclose(fp);
}
②fwrite函数
函数原型:size_t fwrite(void const *Buffer, size_t ElementSize, size_t ElementCount,FILE *fp);
说明:
——功能:将程序的数据结构中的数据以二进制方式输出到文件中
——Buffer:一个泛型const指针,可以接受任意类型的指针
——ElementSize:写入数据每一项的长度
——ElementCount:需要写入数据的项数
——fp:文件指针
例:
#include<stdio.h>
int main(){
FILE *fp;
fp=fopen("file.dat","wb");
if(!fp){
exit(0);
}
int arr[5]={1,2,3,4,5};
fwrite(arr+1,sizeof(int),2,fp); //将arr[1],arr[2]中数据以二进制的形式写入到文件中
fclose(fp);
}
文件的定位
文件中有一个位置指针,指向当前读写位置。如果顺序读写一个文件,每次读写完一个字符后,该位置指针自动指向下一个字符位置。如果想改变这样的规律,强制使位置指针指向指定位置,可以用有关函数。
①rewind函数
函数原型:void rewind(FILE *fp);
说明:
——功能:使位置指针重返回文件的开头
例:
rewind(fp);
②fseek函数
函数原型:int fseek(FILE* Stream, long Offset,int Origin);
说明:
——功能:可以将位置指针指向所需的位置
——Stream:文件指针
——Offset:位移量(以起始点为基准,向前移动的字节数)
——Origin:参考点:
0 或 SEEK_SET 文件开头
1 或 SEEK_CUR 当前位置
2 或 SEEK_END 文件末尾
例:
//假设文件指针fp指向的是一个存放10个整数的二进制文件,文件模式为“rb”,要将读取第5个整数到n
int n;
fseek(fp,sizeof(int)*(5-1),SEEK_SET);//注意位移量的大小
fread(&n,sizeof(int),1,fp);
③feof函数
函数原型:int feof(FILE *fp);
说明:
——功能:检测文件内部位置指针的位置,以确定是否到了文件的末尾。若文件结束,则返回值为1,否则为0。
例:
feof(fp);
四、文件的输入输出流 (C++)
1、文件流对象
IO文件流定义在头文件<fstream>中
#include<fstream>
using namespace std;
创建文件流对象
有三种文件流类型:ifstream(输入文件流),ofstream(输出文件流),fstream(输入输出文件流)
例:
ifstream in; //创建一个文件输入流对象in
ofstream out; //创建一个文件输出流对象out
2、文件操作函数
文件的打开
①文件流的构造函数
可以在创建一个文件流对象时,调用构造函数来将文件流对象与指定文件关联(也就是打开文件)
函数原型:略
说明:
——调用方式:文件流类型 文件流对象名(文件名,文件模式);
——文件名:const char *类型或者const string &类型(在构造函数内部会通过string库函数c_str()将string类型转换为const char *类型)
——文件模式(为默认参数):
每一个文件流都设置一个默认的文件模式,若没有指定文件模式时,以默认的文件模式打开文件。
ifstream类型:ios::in
ofstream类型:ios::out
fstream类型:ios::in|ios::out
ios::in以读方式打开文件
ios::out以写方式打开文件(若已经有此文件,则将其原有内容清空,若文件不存在,则建新文件)
ios::app以写方式打开文件(若已经有此文件,则将写入的数据追加到文件末尾,若文件不存在,则建新文件)
ios::ate初始位置为文件末尾(若文件不存在,打开失败)
ios::binary以二进制方式打开一个文件,如果不这样指定默认为以ASCII码方式打开文件(若文件不存在则打开失败)
ios::trunc若文件已经存在则先删除文件中的全部数据
ios::_Nocreate不创建文件,所以文件不存在时打开失败
ios::_Noreplace不改变文件,所以文件已经存在时打开失败
若要指定多个文件模式,各个模式间以" | "隔开
例:
ofstream out("file.txt",ios::app);
②open函数
可以在调用缺省构造函数之后使用open成员函数打开文件(不可在调用构造函数指定了文件名之后且未关闭文件的情况下又使用open函数)
函数原型:略
说明:
——调用方式:文件流对象.open(文件名,文件模式)
——文件名:const char *类型或者const string &类型(在open函数内部会通过string库函数c_str()将string类型转换为const char *类型)
——文件模式(默认参数,同构造函数)
例:
ofstream out;
out.open("file.txt",ios::app);
检验文件是否打开成功:
ofstream obj;
obj.open("myf.txt");
if(!obj){
exit(0);
}
文件的关闭(close函数)
函数原型:void close();
说明:
——调用方式:文件流对象.close();
——特别注意:当读写数据完成之后,需要断开文件流对象与文件的关联,否则与文件流对象关联的文件不可以被其他文件流对象访问,该文件流对象也不能访问其他文件。
文件的读写
(1)输出文件流
①插入运算符" << "
插入运算符是所有标准C++数据类型预先设计好的
例:
int main(){
ofstream obj;
obj.open("myf.txt");
if(!obj){
exit(0);
}
int a=4;
obj<<a;
obj.close();
}
②write函数
函数原型:ostream &write(const char *Str,long long Count);
说明:
——功能:将一串字符写入文件中
——所属:可以看出write是ofstream父类ostream的成员函数
——Str:一个所要写入文件的字符数组
——Count:要写入数据的字节数
例:
int main(){
ofstream out;
out.open("myf.txt",ios::binary);
if(!out){
exit(0);
}
char str[]="lyj";
out.write(str,sizeof(str));//若将第二个参数改为2,则只写入"ly"
out.close();
}
一个强大的功能:write函数当遇到空字符的时候不停止,可以写入完整的类或结构体结构(但此时的写入方式最好为二进制模式ios::binary)
例:
struct student{
char name[20];
int age;
};
int main(){
ofstream out;
student obj={"lyj",19};
out.open("file.dat",ios::binary);
if(!out){
exit(0);
}
out.write((char *)&obj,sizeof(student));
out.close();
}
(2)输入文件流
①提取运算符" >>"
提取运算符对于所有标准C++数据类型都是预先设计好的
例:
//文件myf.txt中数据为整数
int main(){
ifstream in;
in.open("myf.txt");
if(!in){
exit(0);
}
int a;
in>>a;
in.close();
}
②read函数
函数原型:istream &read(char *Str,long long Count);
说明:
——功能:从文件中读出一串字符到Str中
——所属:可以看出write是ifstream父类istream的成员函数
——Str:存放从文件中读取的一串字符
——Count:所要读取的字节数(会读取换行符,不会自动添加结束符‘\0’;在读取完Count个字符之前,只有当遇到文件结束或者在文本模式文件中遇到文件结束标记字符时结束读取。)
例:
int main(){
ifstream in;
in.open("myf.txt");
if(!in){
exit(0);
}
char str[15];
str[7]='\0'; //加上结束符
in.read(str,10);
cout<<str;
in.close();
}
文件的定位
读指针:输入文件流中,指向当前读取数据的位置的指针。
写指针:输出文件流中,指向当前写入数据的位置的指针。
①tellg函数&tellp函数
tellg函数
函数原型:pos_type tellg();
说明:
——功能:返回读指针所指向的位置
——pos_type:整型,代表字节数
——只可以被文件输入流对象调用
tellp函数
函数原型:pos_type tellp();
说明:
——功能:返回写指针所指向的位置
——pos_type:整型,代表字节数
——只可以被文件输出流对象调用
例:
ofstream out("file.txt");
out.tellp(); //显然为0
②seekg函数&seekp函数
seekg函数
函数原型(1):istream& seekg(pos_type pos)
函数原型(2):istream& seekg(off_type Off,ios_base::seekdir Way)
说明:
——所属:可以看出seekp是ifstream父类istream的成员函数
——功能:设置读指针所指向的位置
——其他说明同seekp函数
seekp函数
函数原型(1):ostream& seekp(pos_type pos)
函数原型(2):ostream& seekp(off_type Off,ios_base::seekdir Way)
说明:
——所属:可以看出seekp是ofstream父类ostream的成员函数
——功能:设置写指针所指向的位置
——pos_type:整型
——off_type:整型
——ios_base::seekdir
在ios中定义的枚举类型:
enum seek_dir {beg, cur, end};
——(1)pos:写指针所指向的新位置(距文件流的起始位置的字节数)
——(2)Off:偏移量(字节数)
——(2)Way:参考点
ios::beg:文件流的起始位置
ios::cur:文件流的当前位置
ios::end:文件流的结束位置
例:
ifstream in("file.txt");
in.seekg(11,ios::cur);