C++文件输入输出,看这一篇就够了

1 简介

1.1 继承关系

基类->继承类:

​ ios->ostream->ofstream

​ ios->istream->ifstream

​ iostream->fstream(还继承了两个缓冲区,一个用于输入,一个用于输出)(此类用于文件I/O的同步,即协调地移动输入缓冲区的输入指针和输出缓冲区的输出指针)(输入输出模式时使用fstream类)

1.2 流

C++程序将文件输入和输出看作字符流;对于输入来说,程序从输入流中抽取字符,对于输出来说,程序向输出流中插入字符。流是程序与流源或流目的之间的中介,这样C++就可以对来源不同的字符做相同处理。

1.3 缓冲区

缓冲区就是一块存储空间,它是为了匹配程序处理速度和外设处理速度;比如程序一次处理1byte,但是磁盘一次读取512bytes;又或者程序一次处理1byte,可以1byte地从磁盘读取,但是由于硬件读取一次数据复杂且操作慢,因此使用缓冲区可以加快程序处理速度。

flushing the buffer:刷新缓冲区就是清空缓冲区地内容以备下次使用。

文件输出输入使用缓冲区,在声明每个ofsream or ifsream对象时程序会为其自动分配该对象自己的缓冲区;使用缓冲区可以提高文件输出速度。
何时清空缓存:
1.缓存区满时
2.使用文件close()方法时,为了保证关闭文件时文件被更新。

2 输入输出流状态

流状态被定义为iostate,是由三个ios_base组成:eofbit, badbit, 和failbit,三者都为一位可以设置位1(设置)或0(清除)。当三者都为0时说明一切顺利。

成员 描述
eofbit 当读取文件到达文件尾时置为1
failbit 当输入流中的数据类型和目的数据类型不一致时置为1;I/O错误(读取一个不允许访问的只读磁盘或打开一个不存在的文件)时置为1
badbit 在一些无法诊断的失败破坏流时置为1
goodbit 另一种说明0的方法
good() 如果流可以使用,则返回true,反之返回false
eof() 如果eofbit被设置,则返回true,反之返回false
bad() 如果badbit被设置,则返回true,反之返回false
fail() 如果failbit或badbit被设置,返回true,反之返回false
rdstate() 返回流状态
exceptions() 返回一个位掩码,指出哪些标记导致异常被引发
exceptions(iostate ex) 设置哪些状态将导致clear()引发异常
clear(iostate s) 将流状态设置为s,s的默认值为0;如果rdstate() & exceptions()) != 0,将抛出basic_ios::failure异常;将会设置s,但是其他的会被置0
setstate (iostate s) 调用clear(rdstate()|s)。这将设置与s中设置的位对应的流的状态位,其他流状态保持不变。

2.1 设置状态

setstate()的主要目的是提供一种修改状态的途径。例如,如果num是一个int,则下面的调用将可能导致operator>>(int &)使用setstate()设置failbit或eofbit。

clear();//清除所有的位
clear(eofbit);//清除当前的位并设置为指定的位
setstate(eofbit);//设置指定的位
//等等

2.2 文件尾EOF

文件尾 EOF
如果输入来自于文件:使用文件尾(EOF)判断文件是否读完。
当检测到EOF之后:cin将eofbit和failbit都设置为1;同时,将设置cin对象中的一个指示EOF条件的标记,设置这个标记后,cin将不读取输入,直到cin.clear()。
EOF被定义为值为-1,因此不能将EOF赋给char类型,因为char类型没有符号,需要使用int类型接收EOF。

举例:Windows允许通过键盘模拟文件尾:Ctrl+Z

3 文件传输模式

ios_base定义了openmode类型表示模式,类似于fmtflags和iostate类型,是bitmask类型。

文件模式常量:

Constant Meaning
ios_base::in 读取文件
ios_base::out 写入文件
ios_base::ate 打开文件时文件指针指向文件尾
ios_base::app 在文件尾添加内容
ios_base::trunc 如果文件存在则截断它
ios_base::binary 二进制文件

文件模式组合(|符号表示联合两个二进制为一个值,该值可以表达两个二进制模式)

C++ 模式 C模式 含义
ios_base::in “r” 读文件模式
ios_base::out “w” 与ios_base::out | ios_base::trunc一样
ios_base::out | ios_base::trunc “w” 写文件模式,并且在打开文件时截断文件(将文件置为空)
ios_base::out | ios_base::app “a” 追加模式,将文件指针指向文件尾,向末尾添加内容;原始数据不可修改
ios_base::in | ios_base::out “r+” 打开以读写,在文件允许的位置写入
ios_base::in | ios_base::out | ios_base::trunc “w+” 打开以读写,如果文件已经存在,则首先截断文件
c++mode | ios_base::binary “cmodeb” 在C++模式和二进制模式打开
c++mode | ios_base::ate “cmode” 以指定的模式打开,并将文件指针移到文件尾。

模式的使用方法:

//第二个参数指定模式
//ifstream fin(filename, c++mode);
//fopen(filename, cmode);
ifstream fin("banjo", mode1); // constructor with mode argument
ofstream fout();
fout.open("harp", mode2); // open() with mode arguments

ifstream open()的默认模式为ios_base::in;

ofstream open()的默认模式为ios_base::out | ios_base::trunc;

fstream无默认模式,因此在创建fstream对象时要显示提高模式实参。

4 相关函数

4.1 ofstream对象 or ifstream对象.open(filename)

可以接受C-风格字符串作为参数,也可以接受存储在数组中的字符串
默认情况下,当filename所指文件不存在时,open()将在当前程序可执行目录创建名为filename的文件;
作用:当存在时,open()将首先截断该文件,即删除原有的文件内容,然后再将新内容输出到文件中

4.2 ofstream对象 or ifstream对象.close()

作用:无参数,原因是ofstream对象 or ifstream对象已经关联文件,不需要参数;在关闭文件时,不会释放ofstream对象 or ifstream对象的缓冲区,只是解除了ofstream对象 or ifstream对象与文件的关联关系;ofstream对象 or ifstream对象可以重新与其他文件绑定。

4.3 ofstream对象 or ifstream对象.isopen(filename)

检查文件filename是否被成功打开;如果成功打开,则返回true;反之(文件不存在等原因),返回false。

在流状态中的fin.fail()和fin.good()也能检查文件是否被正常打开,但是流状态相关函数不涉及文件传输模式;但是is_open()考虑了文件传输模式,因此is_open()更适用于检查文件是否正常打开。

4.4 ifstream对象.get(char ch)

作用:逐个字符获取文件中的内容,并将其赋值给ch;直到遇见文件尾。

4.5 ofstream对象.write( (char *) &pl, size);

第一个参数为要写入文件内容的地址(必须转换为char *类型),第二个参数为要写入内容的大小(单位为byte)。

作用:将pl指向的大小为size的内容复制到ofstream对象绑定的文件中。

注意事项:这个方法可以用于不含虚函数的类,原因是在这种情况下,只有数据成员被保存,而方法不会被保存;如果类有虚方法,则也将复制隐藏指针(该指针指向虚函数的指针表);由于下一次运行程序时,虚函数表可能在不同的位置,因此将文件中的旧指针信息复制到对象中,可能造成混乱。

4.6 ifstream对象.read((char *) &pl, size);

第一个参数为读入文件的存储地址(必须转换为char *类型),第二个参数为要读入内容的大小(单位为byte)。

作用:将ifstream对象绑定的文件中大小为size的内容pl指向的地址。

注意事项:这个方法可以用于不含虚函数的类,原因是在这种情况下,只有数据成员被保存,而方法不会被保存;如果类有虚方法,则也将复制隐藏指针(该指针指向虚函数的指针表);由于下一次运行程序时,虚函数表可能在不同的位置,因此将文件中的旧指针信息复制到对象中,可能造成混乱。

4.7 fstream对象 or istream对象.seekg()

将输入指针移动至给定文件位置(由于fstream对象使用缓冲区存取中间数据,因此指针指向的实际上是缓冲区的位置,而不是实际的文件)。

也可以将seekg()用于ifstream对象。

函数原型:

第一个原型定位到离第二个参数指定的文件位置的偏移量(单位为字节)的位置,第一个参数off_type为偏移量,第二个参数ios_base::seekdir可以为三个常量—ios_base::beg(相对于文件开始处的偏移)、ios_base::cur(相对于文件当前位置的偏移)、ios_base::end(相对于文件结尾处的偏移);

第二个原型定位到离文件开头特定距离(单位为字节)的位置,参数pos_type为偏移量。是一个绝对偏移量。

//模板原型
basic_istream<charT,traits>& seekg(off_type, ios_base::seekdir);
basic_istream<charT,traits>& seekg(pos_type);
//For the char specialization
istream & seekg(streamoff, ios_base::seekdir);
istream & seekg(streampos);//streampos包含一个接收streamoff参数的构造函数和一个接收整数参数的构造函数

//eg
fin.seekg(30, ios_base::beg); // 30 bytes beyond the beginning
fin.seekg(-1, ios_base::cur); // back up one byte
fin.seekg(0, ios_base::end); // go to the end of the file

注意:streampos和streampos是off_type和pos_type对于char类型的专门化;同样,针对wistream对象,wstreampos和streamoff类型是off_type和pos_type对于wistream类型的专门化。

4.8 fstream对象 or ostream对象.seekp()

将输出指针移动至给定文件位置(由于fstream对象使用缓冲区存取中间数据,因此指针指向的实际上是缓冲区的位置,而不是实际的文件)。

也可以将seekp()用于ofstream对象。

将输出指针移动至给定文件位置(由于fstream对象使用缓冲区存取中间数据,因此指针指向的实际上是缓冲区的位置,而不是实际的文件)。

也可以将seekp()用于ofstream对象。

函数原型:

第一个原型定位到离第二个参数指定的文件位置的偏移量(单位为字节)的位置,第一个参数off_type为偏移量,第二个参数ios_base::seekdir可以为三个常量—ios_base::beg(相对于文件开始处的偏移)、ios_base::cur(相对于文件当前位置的偏移)、ios_base::end(相对于文件结尾处的偏移);

第二个原型定位到离文件开头特定距离(单位为字节)的位置,参数pos_type为偏移量。是一个绝对偏移量。

//模板原型
basic_istream<charT,traits>& seekp(off_type, ios_base::seekdir);
basic_istream<charT,traits>& seekp(pos_type);
//For the char specialization
istream & seekp(streamoff, ios_base::seekdir);
istream & seekp(streampos);//streampos包含一个接收streamoff参数的构造函数和一个接收整数参数的构造函数

//eg
fin.seekp(30, ios_base::beg); // 30 bytes beyond the beginning
fin.seekp(-1, ios_base::cur); // back up one byte
fin.seekp(0, ios_base::end); // go to the end of the file

注意:streampos和streampos是off_type和pos_type对于char类型的专门化;同样,针对wistream对象,wstreampos和streamoff类型是off_type和pos_type对于wistream类型的专门化。

4.9 fstream对象 or istream对象.tellg()

返回值为streampos,表示输入指针的当前文件位置(单位为字节),相对于文件开始的偏移量。

注意:如果创建了fstream对象,由于输入指针和输出指针将同时移动,因此tellg()和tellp()返回值相同;然而,如果使用istream对象来管理输入流,而使用ostream对象管理输出流,则输入指针和输出指针将彼此独立移动,tellg()和tellp()返回值将不同。

4.10 fstream对象 or ostream对象.tellp()

返回值为streampos,表示输出指针的当前文件位置(单位为字节),相对于文件开始的偏移量。

注意:如果创建了fstream对象,由于输入指针和输出指针将同时移动,因此tellg()和tellp()返回值相同;然而,如果使用istream对象来管理输入流,而使用ostream对象管理输出流,则输入指针和输出指针将彼此独立移动,tellg()和tellp()返回值将不同。

4.11 fstream对象 or ostream对象 or istream对象.seekg(偏移量)

将文件指针指向距离文件开头 偏移量(单位为字节) 大小的位置。

可以使用它将文件指针指向文件开头。

5 简单文件输入输出

5.1 写入到文本文件中

步骤:

  • 1 包含fstream头文件,fstream头文件中定义了ofstream类用于处理输出
  • 2 包含std命名空间(ofstream在std命名空间中)
  • 3 定义一个或多个ofstream对象,命名要求符合命名规范,每个ofstream对象都与一个输出流绑定在一起
  • 4 将对象和文件关联起来,方法之一是open()方法,这种关联在对象释放时自动解除
  • 5 文件操作完成时,应使用close()方法关闭文件
  • 6 可以使用ofstream对象和<<操作符输出各种类型的数据,可以使用cout可使用的任何方法(cout使用方法见C++ cout的用法,看这一篇就够了)

5.2 读取文本文件

步骤:

  • 1 包含fstream头文件,fstream头文件中定义了ifstream类用于处理输入,ifstream与一个输入流绑定在一起
  • 2 包含std命名空间(ifstream在std命名空间中)
  • 3 定义一个或多个ifstream对象,命名要求符合命名规范,每个ofstream对象都与一个输入流绑定在一起
  • 4 将对象和文件关联起来,方法之一是open()方法
  • 5 文件操作完成时,使用close()方法关闭文件
  • 6 可以使用ifstream对象和>>操作符输出各种类型的数据,可以使用cin可使用的任何方法(cin使用方法见C++ cin的用法,看这一篇就够了)
  • 7 ifstream本身作为测试条件时,如果最后一个读取操作成功,ifstream将被转换为true,否则转换为false

5.3 C++实例

5.3.1 cpp代码

/*
Project name :			_26File
Last modified Date:		2022年4月29日22点20分
Last Version:			V1.0
Descriptions:			简单文件输入输出
*/
#include 
#include  // for file I/O,这个头文件包含了iostream头文件,所以此处可以不显示包含iostream头文件
#include  // support for exit()
const int SIZE = 60;
int main()
{
	using namespace std;
	cout << "ofstream*********************************************************************" << endl;
	char automobile[50];
	int year;
	double a_price;
	double d_price;
	string filename1;
	cout << "Enter name for new file: ";
	cin >> filename1;
	while (cin.get() != '\n');
	ofstream outFile; // create object for output
	outFile.open(filename1.c_str()); // associate with a file  将文件和对象链接起来 
	//创建对象和连接文件可以使用一个表达式实现:ofstream fout("jar.txt"); // create fout object, associate it with jar.txt
	cout << "Enter the make and model of automobile: ";
	cin.getline(automobile, 50);
	cout << "Enter the model year: ";
	cin >> year;
	cout << "Enter the original asking price: ";
	cin >> a_price;
	d_price = 0.913 * a_price;
	// display information on screen with cout
	cout << fixed;
	cout.precision(2);
	cout.setf(ios_base::showpoint);
	cout << "Make and model: " << automobile << endl;
	cout << "Year: " << year << endl;
	cout << "Was asking $" << a_price << endl;
	cout << "Now asking $" << d_price << endl;
	// now do exact same things using outFile instead of cout
	outFile << fixed;
	outFile.precision(2);
	outFile.setf(ios_base::showpoint);
	outFile << "Make and model: " << automobile << endl;
	outFile << "Year: " << year << endl;
	outFile << "Was asking $" << a_price << endl;
	outFile << "Now asking $" << d_price << endl;
	outFile.close(); // done with file
	cout << "ifstream*********************************************************************" << endl;
	while (cin.get() != '\n');
	char filename[SIZE];
	ifstream inFile; // object for handling file input
	cout << "Enter name of data file: ";
	cin.getline(filename, SIZE);
	inFile.open(filename); // associate inFile with a file
	//创建对象和连接文件可以使用一个表达式实现 ifstream inFile(filename);
	if (!inFile.is_open()) // failed to open file
	{
		cout << "Could not open the file " << filename << endl;
		cout << "Program terminating.\n";
		exit(EXIT_FAILURE);//EXIT_FAILURE就是一个全局的常量
		//exit():定义在头文件cstdlib,该头文件还定义了与操作系统通信的参数值EXIT_FAILURE
	}
	double value;
	double sum = 0.0;
	int count = 0; // number of items read
	while (inFile>>value) // 判断读取是否成功,读取失败则退出循环
	{
		++count; // one more item read
		sum += value; // calculate running total
	}
	//因此在这加两句读取最后一个数据
	++count; // one more item read
	sum += value; // calculate running total
	//检查读取失败的原因
	if (inFile.eof())
		cout << "End of file reached.\n";
	else if (inFile.fail())
		cout << "Input terminated by data mismatch.\n";
	else
		cout << "Input terminated for unknown reason.\n";
	if (count == 0)
		cout << "No data processed.\n";
	else
	{
		cout << "Items read: " << count << endl;
		cout << "Sum: " << sum << endl;
		cout << "Average: " << sum / count << endl;
	}
	inFile.close(); // finished with the file

	return 0;
}

5.3.2 运行结果

ofstream*********************************************************************
Enter name for new file: list.txt
Enter the make and model of automobile: mobile
Enter the model year: 2022
Enter the original asking price: 999
Make and model: mobile
Year: 2022
Was asking $999.00
Now asking $912.09
ifstream*********************************************************************
Enter name of data file: scores.txt
End of file reached.
Items read: 13
Sum: 222.00
Average: 17.08

D:\Prj\_C++Self\_26File\Debug\_26File.exe (进程 5572)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

5.3.3 相关文件信息

写入的文件内容:list.txt

Make and model: mobile
Year: 2022
Was asking $999.00
Now asking $912.09

读入的文件内容:scores.txt

18 19 18.5 13.5 14 16 19.5 20 18 12 18.5
17.5

6 命令行处理技术

6.1 简介

命令行处理程序经常使用命令行参数识别文件,命令行参数是指出现在当在command使用命令行时出现的参数。举例:

wc report1 report2 report3
此处wc是程序名,report1 report2 report3是文件名作为命令行参数传递给程序

命令行处理技术也可以在main中使用:

int main(int argc, char *argv[])
//argc代表命令行中出现的参数个数,argv是指向 chart * 的指针;argv相当于指针数组,每个指针指向一个命令行参数;argv[0]是命令名,argv[1]指向第一个命令行参数字符串,以此类推

注意事项:命令行参数与命令行操作系统息息相关;很多Windows IDEs提供了命令行参数的选项,如何打开需要研读相关说明文档;很多Windows IDEs都可以生成可执行我呢见,这些文件可以在Windows命令提示符模式下运行。

6.2 C++实例

6.2.1 代码

项目_26_1file_command_arguments

/*
Project name :			_26_1file_command_arguments
Last modified Date:		2022年4月30日09点53分
Last Version:			V1.0
Descriptions:			文件操作
*/

#include 
#include  // for file I/O,这个头文件包含了iostream头文件,所以此处可以不显示包含iostream头文件
#include  // support for exit()
int main(int argc, char* argv[])
{
	using namespace std;
	
	cout << "命令行处理技术**************************************************************" << endl;
	if (argc == 1) // quit if no arguments
	{
		cerr << "Usage: " << argv[0] << " filename[s]\n";
		exit(EXIT_FAILURE);
	}
	ifstream fin; // open stream
	long count1;
	long total = 0;
	char ch;
	for (int file = 1; file < argc; file++)
	{
		fin.open(argv[file]); // connect stream to argv[file]
		if (!fin.is_open())
		{
			cerr << "Could not open " << argv[file] << endl;
			fin.clear();
			continue;
		}
		count1 = 0;
		while (fin.get(ch))
			count1++;
		cout << count1 << " characters in " << argv[file] << endl;
		total += count1;
		fin.clear(); // needed for some implementations
		fin.close(); // disconnect file
	}
	cout << total << " characters in all files\n";

	return 0;
}

6.2.2 如何执行该代码

首先在VS2022中编译该代码,生成_26_1file_command_arguments.exe可执行文件
C++文件输入输出,看这一篇就够了_第1张图片然后在cmd中找到_26_1file_command_arguments.exe可执行文件并执行以下语句将会得到以下结果:

Microsoft Windows [版本 10.0.19044.1645]
(c) Microsoft Corporation。保留所有权利。
C:\Users\15495>d:
D:\>cd D:\Prj\_C++Self\_26_1file_command_arguments\x64\Debug
D:\Prj\_C++Self\_26_1file_command_arguments\x64\Debug>_26_1file_command_arguments.exe "list.txt" "carinfo.txt" "scores.txt"
命令行处理技术**************************************************************
Make and model: love
Year: 2022
Was asking $999.00
Now asking $912.09
70 characters in list.txt
Make and model: kaaha
Year: 2022
Was asking $999.00
Now asking $912.09
71 characters in carinfo.txt
18 19 18.5 13.5 14 16 19.5 20 18 12 18.5
17.545 characters in scores.txt
186 characters in all files

D:\Prj\_C++Self\_26_1file_command_arguments\x64\Debug>

6.3.3 相关文件信息

carinfo.txt

Make and model: kaaha
Year: 2022
Was asking $999.00
Now asking $912.09

list.txt

Make and model: love
Year: 2022
Was asking $999.00
Now asking $912.09

scores.txt

18 19 18.5 13.5 14 16 19.5 20 18 12 18.5
17.5

7 向文件追加内容(追加模式举例)

7.1 代码

/*
Project name :			_26File
Last modified Date:		2022年4月30日10点05分
Last Version:			V1.0
Descriptions:			向文件追加内容
*/

#include 
#include  // for file I/O,这个头文件包含了iostream头文件,所以此处可以不显示包含iostream头文件
#include  // support for exit()
#include 
int main(int argc, char* argv[])
{
	using namespace std;
	cout << "向文件追加内容*********************************************************************" << endl;
	char ch;
	const char* file = "guests.txt";
	// show initial contents
	ifstream fin;
	fin.open(file);
	if (fin.is_open())
	{
		cout << "Here are the current contents of the "
			<< file << " file:\n";
		while (fin.get(ch))
			cout << ch;
		fin.close();
	}
	// add new names
	ofstream fout(file, ios::out | ios::app);//追加模式
	if (!fout.is_open())
	{
		cerr << "Can't open " << file << " file for output.\n";
		exit(EXIT_FAILURE);
	}
	cout << "Enter guest names (enter a blank line to quit):\n";
	string name;
	while (getline(cin, name) && name.size() > 0)
	{
		fout << name << endl;
	}
	fout.close();
	// show revised file
	fin.clear(); // not necessary for some compilers
	fin.open(file);
	if (fin.is_open())
	{
		cout << "Here are the new contents of the "
			<< file << " file:\n";
		while (fin.get(ch))
			cout << ch;
		fin.close();
	}

	return 0;
}

7.2 运行结果

向文件追加内容*********************************************************************
Here are the current contents of the guests.txt file:
Jasmine
lili
bobo
booooo
boly
Enter guest names (enter a blank line to quit):
mama
baba

Here are the new contents of the guests.txt file:
Jasmine
lili
bobo
booooo
boly
mama
baba

8 二进制文件

8.1 二进制文件与文本文件

8.1.1 文本文件优缺点

优点:

  • 易于阅读,可以使用文本编辑器或word阅读或修改文本文件
  • 文本文件在计算机之间容易传输

缺点:如果将数字存储为文本格式的话,需要<<插入操作符将数字转换为字符才可存储到txt文件中。

8.1.2 二进制格式文件优缺点

优点:

  • 二进制格式对于数字来说更精确,因为它存储的是值的内部表示,因此不会有转换误差和舍入误差
  • 以二进制格式保存数据速度更快
  • 二进制格式占用空间更小

缺点:二进制格式在计算机之间传输可能存在问题,因为不同计算机的内部存储格式可能不一样;这种情况下需要编写一个程序将一种数据格式转换为另一种数据格式。

8.1.3 存储格式与文件格式

使用二进制文件模式时,程序将数据从内存传输给文件(反之亦然)时,将不会发生任何隐藏的转换。

Windows文本文件,使用回车和换行表示换行符;

Mscintosh文本文件,使用回车表示换行符;

UNIX和Linux文本文件,使用换行表示换行符;

C++从UNIX发展而来,使用换行表示换行符;

Windows C++程序在写文本文件时,自动将C++换行符转换为回车和换行;在读取文本文件时,将回车和换行转换为C++换行符。

对于二进制数据,如果使用文本格式文件存储,可能会出现问题,double值中间的字节可能与换行符的ASCII码有相同的位模式;另外,文件尾的检测也存在区别;因此以二进制格式保存数据时,应使用二进制文件模式。

8.2 C++实例

8.2.1 代码

/*
Project name :			_26File
Last modified Date:		2022年4月30日11点39分
Last Version:			V1.0
Descriptions:			二进制文件存储
*/

#include 
#include  // for file I/O,这个头文件包含了iostream头文件,所以此处可以不显示包含iostream头文件
#include  // support for exit()
#include 
#include 
const int SIZE = 60;

//用于二进制文件存储
inline void eatline() { while (std::cin.get() != '\n') continue; }
struct planet
{
	char name[20]; // name of planet
	double population; // its population
	double g; // its acceleration of gravity
};

int main()
{
	using namespace std;
	cout << "二进制文件存储*******************************************************************" << endl;
	char file[30] = "planets.dat";
	planet pl;
	cout << fixed << right;
	// show initial contents
	ifstream fin;
	fin.open(file, ios_base::in | ios_base::binary); // binary file
	//NOTE: some systems don't accept the ios_base::binary mode
	if (fin.is_open())
	{
		cout << "Here are the current contents of the "
			<< file << " file:\n";
		while (fin.read((char*)&pl, sizeof pl))
		{
			cout << setw(20) << pl.name << ": "
				<< setprecision(0) << setw(12) << pl.population
				<< setprecision(2) << setw(6) << pl.g << endl;
		}
		fin.close();
	}
	// add new data
	ofstream fout1(file, ios_base::out | ios_base::app | ios_base::binary);
	//NOTE: some systems don't accept the ios::binary mode
	if (!fout1.is_open())
	{
		cerr << "Can't open " << file << " file for output:\n";
		exit(EXIT_FAILURE);
	}
	cout << "Enter planet name (enter a blank line to quit):\n";
	cin.get(pl.name, 20);
	while (pl.name[0] != '\0')
	{
		eatline();
		cout << "Enter planetary population: ";
		cin >> pl.population;
		cout << "Enter planet's acceleration of gravity: ";
		cin >> pl.g;
		eatline();
		fout1.write((char*)&pl, sizeof pl);
		cout << "Enter planet name (enter a blank line to quit):\n";
		cin.get(pl.name, 20);
	}
	fout1.close();
	// show revised file
	fin.clear(); // not required for some implementations, but won't hurt
	fin.open(file, ios_base::in | ios_base::binary);
	if (fin.is_open())
	{
		cout << "Here are the new contents of the "
			<< file << " file:\n";
		while (fin.read((char*)&pl, sizeof pl))
		{
			cout << setw(20) << pl.name << ": "
				<< setprecision(0) << setw(12) << pl.population
				<< setprecision(2) << setw(6) << pl.g << endl;
		}
		fin.close();
	}

	return 0;
}

8.2.2 运行结果

二进制文件存储*******************************************************************
Here are the current contents of the planets.dat file:
               earth:          70.  9.98
Enter planet name (enter a blank line to quit):
viras
Enter planetary population: 20
Enter planet's acceleration of gravity: 6.77
Enter planet name (enter a blank line to quit):

Here are the new contents of the planets.dat file:
               earth:          70.  9.98
               viras:          20.  6.77

D:\Prj\_C++Self\_26File\Debug\_26File.exe (进程 5992)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

9 随机存取

9.1 简介

随机存取指的是直接移动到文件的任何位置;常用于数据库文件,程序维护一个独立的索引文件,该文件指出数据在主数据文件中的位置。如果文件由长度相同的记录组成,这种方法实现起来最简单;每条记录表示一组相关的数据。

9.2 随机位置输入输出实例–修改记录

9.2.1 代码

/*
Project name :			_26File
Last modified Date:		2022年4月30日21点49分
Last Version:			V1.0
Descriptions:			随机位置输入输出实例
*/

#include 
#include  // for file I/O,这个头文件包含了iostream头文件,所以此处可以不显示包含iostream头文件
#include  // support for exit()
#include 
#include 
const int SIZE = 60;
const int LIM = 20;

//用于二进制文件存储
inline void eatline() { while (std::cin.get() != '\n') continue; }
struct planet
{
	char name[LIM]; // name of planet
	double population; // its population
	double g; // its acceleration of gravity
};

int main()
{
	using namespace std;
	cout << "随机位置输入输出文件*********************************************************" << endl;
	const char* file = "planets.dat";
	planet pl;
	cout << fixed;
	// show initial contents
	fstream finout; // read and write streams
	finout.open(file,ios_base::in | ios_base::out | ios_base::binary);//输入输出模式打开
	//NOTE: Some Unix systems require omitting | ios::binary
	int ct = 0;
	if (finout.is_open())
	{
		finout.seekg(0); // go to beginning
		cout << "Here are the current contents of the " << file << " file:\n";
		while (finout.read((char*)&pl, sizeof pl))
		{
			cout << ct++ << ": " << setw(LIM) << pl.name << ": "
				<< setprecision(0) << setw(12) << pl.population
				<< setprecision(2) << setw(6) << pl.g << endl;
		}
		if (finout.eof())
			finout.clear(); // clear eof flag
		else
		{
			cerr << "Error in reading " << file << ".\n";
			exit(EXIT_FAILURE);
		}
	}
	else
	{
		cerr << file << " could not be opened -- bye.\n";
		exit(EXIT_FAILURE);
	}
	// change a record
	cout << "Enter the record number you wish to change: ";
	long rec;
	cin >> rec;
	eatline(); // get rid of newline
	if (rec < 0 || rec >= ct)
	{
		cerr << "Invalid record number -- bye\n";
		exit(EXIT_FAILURE);
	}
	streampos place = rec * sizeof pl; // convert to streampos type
	finout.seekg(place); // random access 随机访问到指定位置
	if (finout.fail())
	{
		cerr << "Error on attempted seek\n";
		exit(EXIT_FAILURE);
	}
	finout.read((char*)&pl, sizeof pl);
	cout << "Your selection:\n";
	cout << rec << ": " << setw(LIM) << pl.name << ": "
		<< setprecision(0) << setw(12) << pl.population
		<< setprecision(2) << setw(6) << pl.g << endl;
	if (finout.eof())
		finout.clear(); // clear eof flag
	cout << "Enter planet name: ";
	cin.get(pl.name, LIM);
	eatline();
	cout << "Enter planetary population: ";
	cin >> pl.population;
	cout << "Enter planet's acceleration of gravity: ";
	cin >> pl.g;
	finout.seekp(place); // go back
	finout.write((char*)&pl, sizeof pl) << flush;
	if (finout.fail())
	{
		cerr << "Error on attempted write\n";
		exit(EXIT_FAILURE);
	}
	// show revised file
	ct = 0;
	finout.seekg(0); // go to beginning of file
	cout << "Here are the new contents of the " << file << " file:\n";
	while (finout.read((char*)&pl, sizeof pl))
	{
		cout << ct++ << ": " << setw(LIM) << pl.name << ": "
			<< setprecision(0) << setw(12) << pl.population
			<< setprecision(2) << setw(6) << pl.g << endl;
	}
	finout.close();

	return 0;
}

9.2.2 运行结果

随机位置输入输出文件*********************************************************
Here are the current contents of the planets.dat file:
0:                earth:           70  9.98
1:                 Boly:            4  9.99
2:                earth:           80  9.98
Enter the record number you wish to change: 2
Your selection:
2:                earth:           80  9.98
Enter planet name: Jasminely
Enter planetary population: 4
Enter planet's acceleration of gravity: 9.99
Here are the new contents of the planets.dat file:
0:                earth:           70  9.98
1:                 Boly:            4  9.99
2:            Jasminely:            4  9.99

D:\Prj\_C++Self\_26File\Debug\_26File.exe (进程 16340)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

10 生成不同的文件名

10.1 简介

//tmpnam函数生成临时文件名并将其放置到pszName指向的存储空间中;定义在头文件cstdio中的常量L_tmpnam限制了文件名包含的字符数,TMP_MAX限制了在生成文件名不重复的条件下tmpnam()函数可被调用的最多次数。
char* tmpnam( char* pszName );

10.2 代码

#include 
#include 
int main()
{
	using namespace std;
	cout << "This system can generate up to " << TMP_MAX
		<< " temporary names of up to " << L_tmpnam
		<< " characters.\n";
	char pszName[L_tmpnam] = { '\0' };
	cout << "Here are ten names:\n";
	for (int i = 0; 10 > i; i++)
	{
		tmpnam(pszName);
		cout << pszName << endl;
	}
	return 0;
}

10.3 运行结果

每次运行的结果不同。

生成不同的文件名*************************************************************
This system can generate up to 2147483647 temporary names of up to 260 characters.
Here are ten names:
C:\Users\15495\AppData\Local\Temp\u994.0
C:\Users\15495\AppData\Local\Temp\u994.1
C:\Users\15495\AppData\Local\Temp\u994.2
C:\Users\15495\AppData\Local\Temp\u994.3
C:\Users\15495\AppData\Local\Temp\u994.4
C:\Users\15495\AppData\Local\Temp\u994.5
C:\Users\15495\AppData\Local\Temp\u994.6
C:\Users\15495\AppData\Local\Temp\u994.7
C:\Users\15495\AppData\Local\Temp\u994.8
C:\Users\15495\AppData\Local\Temp\u994.9

D:\Prj\_C++Self\_26File\Debug\_26File.exe (进程 11992)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

你可能感兴趣的:(c++,文件,输入输出流)