文件的输入和输出

2023-02-26 11:00:41 浏览数 (1)

1. 简单的文件I/O

写入文件

让程序写入文件,其步骤大致为如下:

  1. 创建一个ofstream对象来管理输入流;
  2. 将该对象与特定的文件关联起来;
  3. 用使用cout的方式使用该对象,唯一的区别是输出将进入文件,而不是屏幕。

例子:

代码语言:javascript复制
//需要包含头文件fstream

ofstream fout;     //#1
fout.open("a.txt");//#2 若a.txt不存在,则会新建一个,若存在,则会清空源文件内容
fout << "hello";   //#3 将hello写入a.txt中

:以默认模式打开文件(即上述方式)进行输出将自动把文件的长度截短为零,这相当于删除已有的内容。具体原因见本文第三节文件模式中的注】

读取文件

读取文件的要求与写入文件相似:

  1. 创建一个ifstream对象来管理输入流;
  2. 将该对象与特定的文件关联起来;
  3. 以使用cin的方式使用该对象。

例子:

代码语言:javascript复制
//需要包含头文件fstream

ifstream fin;     //#1
fin.open("a.txt");//#2 
char ch;
fin >> ch;        //#3 读取一个字符放入ch

:当输入和输出流对象过期(如程序终止)时,到文件的连接将自动关闭。另外,也可以使用close()方法来显式地关闭到文件的连接。关闭这样的连接并不会删除流,而只是断开流到文件的连接。然而,流管理装置仍被保留。】

流状态检查

C 文件流类从ios_base类那里继承了一个流状态成员。检查文件是否成功打开的常见方式如下:

代码语言:javascript复制
ifstream fin;
fin.open(argv[file]);

if (fin.fail()){...} //判断文件打开是否成功
if (!fin.good()){...} //判断文件打开是否成功
if (!fin){...} //判断文件打开是否成功

//is_open()能够检测到这种错误以及good()能够检测到的错误。然而,老式C  实现没有is_open( )。
if (!fin.is_open()){...} //判断文件打开是否成功

2. 命令行处理技术

文件处理程序通常使用命令行参数来指定文件。例如:

代码语言:javascript复制
exc a.txt b.txt

exc为程序可执行文件名,a.txtb.txt为程序exc执行过程中要使用到的文件。要实现上述功能,则程序的主函数应该写为:

代码语言:javascript复制
int main(int argc, char* argv[])

argc为命令行中的参数个数,其中包括命令名本身。argv变量为一个指针,它指向一个指向char的指针。这过于抽象,但可以将argv看作一个指针数组,其中的指针指向命令行参数,argv[0]是一个指针,指向存储第一个命令行参数的字符串的第一个字符,依此类推。也就是说,argv[0]是命令行中的第一个字符串,依此类推。因此上面的例子中:argc为3,argv[0]excargv[1]a.txtargv[2]b.txt

3. 文件模式

文件模式描述的是文件将被如何使用:读、写、追加等。将流与文件关联时(无论是使用文件名初始化文件流对象,还是使用open()方法),都可以提供指定文件模式的第二个参数:

代码语言:javascript复制
ifstream fin("banjo", mode1); // mode1为文件模式
ofstream fout();
fout.open("harp", mode2); // mode2为文件模式

文件模式的常量有:

常量

含义

ios_base::in

打开文件,以便读取

ios_base::out

打开文件,以便写入

ios_base::ate

打开文件,并移到文件尾

ios_base::app

追加到文件尾

ios_base::trunc

如果文件存在,则截短文件

ios_base::binary

二进制文件

ifstream open()方法和构造函数用ios_base::in(打开文件以读取)作为模式参数的默认值,而ofstream open()方法和构造函数用ios_base::out | ios_base::trunc(打开文件,以读取并截短文件,也就是说,其以前的内容将被删除)作为默认值。位运算符OR(|)用于将两个位值合并成一个可用于设置两个位的值。fstream类不提供默认的模式值,因此在创建这种类的对象时,必须显式地提供模式。】

如果要保留文件内容,并在文件尾添加(追加)新信息,则可以使用ios_base::app模式:

代码语言:javascript复制
ofstream fout("bagels", ios_base::out | ios_base::app);//使用|运算符来合并模式,启用模式out和app

老式C 实现之间可能有一些差异。例如,有些实现允许省略前一例子中的ios_base::out,有些则不允许。如果不使用默认模式,则最安全的方法是显式地提供所有的模式元素。

要以二进制格式(而不是文本格式)存储数据,可以使用ofstream对象的write()成员函数。该方法将内存中指定数目的字节复制到文件中。若从二进制文件读取数据,可以使用ifstream对象的read( )成员函数。该方法从文件中内容复制到目标结构中。这两者的对应的使用方法如下:

代码语言:javascript复制
const int LIM = 20;
struct planet
{
    char name[LIM]; // name of planet
    double population; // its population
    double g; // its acceleration of gravity
};
planet pl;
planet p2;

//写入
//ofstream fout("planets.dat", ios_base:: out | ios_base::app);
//fout << pl.name << " " << pl.population << " " << pl.g << "n";
ofstream fout("planets.dat",
              ios_base:: out | ios_base::app | ios_base::binary);
fout.write( (char *) &pl, sizeof(pl));

//读取
ifstream fin("planets.dat", ios_base::in | ios_base::binary);
fin.read((char *) &p2, sizeof(p2));

4. 随机存取

随机存取指的是直接移动(不是依次移动)到文件的任何位置。要实现读/写的效果,需要同时使用in模式和out模式将得到读/写模式,要使用|运算符来组合模式。因此,需要使用下面的语句:

代码语言:javascript复制
fstream finout;//fstream类是从iostream类派生而来的,而后者基于istream和ostream两个类,因此它继承了它们的方法。它还继承了两个缓冲区,一个用于输入,一个用于输出,并能同步化这两个缓冲区的处理。
finout.open(file, ios_base::in | ios_base::out | ios_base::binary);

接下来,需要一种在文件中移动的方式。fstream类为此继承了两个方法:seekg()seekp(),前者将输入指针移到指定的文件位置,后者将输出指针移到指定的文件位置(实际上,由于fstream类使用缓冲区来存储中间数据,因此指针指向的是缓冲区中的位置,而不是实际的文件)。也可以将seekg()用于ifstream对象,将seekp()用于oftream对象。

seekg()seekp()的原型及使用方法如下:

代码语言:javascript复制
//seekg()
istream & seekg(streamoff, ios_base::seekdir);//第一个原型定位到离第二个参数指定的文件位置特定距离(单位为字节)的位置;streamoff值被用来度量相对于文件特定位置的偏移量(单位为字节)。
istream & seekg(streampos);//第二个原型定位到离文件开头特定距离(单位为字节)的位置。streampos对应的是距离(单位为字节)。

//seek_dir参数是ios_base类中定义的另一种整型,有3个可能的值:
//常量ios_base::beg指相对于文件开始处的偏移量;
//常量ios_base::cur指相对于当前位置的偏移量;
//常量ios_base::end指相对于文件尾的偏移量。
fin.seekg(30, ios_base::beg); //将读取位置设置为从文件开头开始的第31个字节(字节编号为30)
fin.seekg(-1, ios_base::cur); //将读取位置设置为从当前位置后退1个字节
fin.seekg(0, ios_base::end); //将读取位置设置为从文件尾0个字节的位置

fin.seekg(112); //第一个字节的编号为0。因此该语句将读取位置设置为第112个字节,也就是文件中的第113个字节



//seekp()
ostream & seekp(streamoff offset,seek_dir origin); 
ostream & seekp (streampos pos);

fout.seekp(30, ios_base::beg); //将写入位置设置为从文件开头开始的第31个字节(字节编号为30)
fout.seekp(-1, ios_base::cur); //将写入位置设置为从当前位置后退1个字节
fout.seekp(0, ios_base::end); //将写入位置设置为从文件尾0个字节的位置

fout.seekp(112); //第一个字节的编号为0。因此该语句将写入位置设置为编号为112的字节,也就是文件中的第113个字节

:如果要检查文件指针的当前位置,则对于输入流,可以使用tellg()方法,对于输出流,可以使用tellp()方法。它们都返回一个表示当前位置的streampos值(以字节为单位,从文件开始处算起)。】

seekg()seekp()函数提供对文件的随机存取。这些类方法使得能够将文件指针放置到相对于文件开头、文件尾和当前位置的某个位置。tellg()tellp()方法报告当前的文件位置。

参考文献 C Primer Plus(第六版) - 第17章 输入、输出和文件

0 人点赞