IO流与文件的操作

1.输入输出概念

标准输入输出,简称标准I/O,也就是键盘输入数据,从屏幕输入数据
文件输入输出,简称文件I/O, 从磁盘文件输入数据,将结果输出到磁盘文件。

2.输入输出流

流(stream):数据像水一样,从一个地方到达另外一个地方。
C++会在内存中会将每一个数据开辟叫缓冲区的地方,用于存放流数据。
比如cout和输出运算符"<<"向显示器输出数据,实际上先将数据输送到缓冲区里面取,直到缓冲区存满或者遇到end或程序结束,这时从缓冲区中数据输送到显示器显示出来。

输出数据存放顺序: cout <<   缓冲区   显示器
而输入数据时候,先通过键盘输入数据到键盘缓冲区,当按回车时候,从键盘缓冲区输送到输入缓冲区, 形成cin流, 然后用“>>”从输入缓冲区将数据传送到程序中关联变量

输入数据存放顺序: 键盘缓冲区  输入缓冲区  通过输入流  程序关联变量

2.1.I/O库常见的流类

ios: 抽象基类, ios派生出类istream和类ostream
istream: 输入流类, 支持输入操作
ostream: 输出流类, 支持输出操作
iostream: 输入输出流类, 由类istreamhe 类ostream派生,支持输入输出操作
ifstream: 输入文件流类, 由类istream所派生, 支持文件输入操作
ofstream:输出文件流类, 由类ostream所派生, 支持文件输出操作
fstream: 输出输入文件流类,由类iostream所派生, 支持文件输入输出操作
如图:
在这里插入图片描述

2.2.IO库常用流类的头文件

iostream(iostream.h):包括类ios,类istream,类ostream,类iostream。
fstream(fstream.h):包括类ifstream,类ofstream和类fstream
iomanip(iomanip.h):输入流控制符应包含此文件中

3.标准输出流对象

3.1.cout

cout是consle pouput的英文缩写,表示终端显示器的输出,cout是输出流类ostream的对象,cout先将数据输送到缓冲区中,然后从缓冲区到终端显示器上。
例:

cout << "我" << "爱学习" << "C++" << endl;

在执行时将"我"、“爱学习”、“C++”、endl依次到缓冲区中,当缓冲区存满或者遇到end或程序结束,这时从缓冲区中数据输送到显示器显示出来。

3.2格式输出

在之前输出数据时候,没有指定输出格式, 由系统根据数据类型默认的格式输出,但有时候想让数据按照指定的格式输出,比如想让小数保留两位在输出。
输出的格式状态是在类ios中定义的枚举值。
如果表示格式状态时候,需要在前面加类ios的作用域运算符::
                   常用输出格式状态

输出格式状态功能输出格式状态功能
ios::left输出数据左对齐ios::scientific浮点数以科学计数法格式输出
ios::right输出数据右对齐ios::fixed浮点数以定点格式(小数形式)输出

输出流控制符是在头文件iomanip(iomanip.h)中定义,输出流控制符如下表:

                   常用输出流控制符

输入流控制符功能
setprecision(n)设置实数精度为n位。在以一般十进制小数形式输出时n代表有效数字。在以定点格式和科学记数法格式输出时n位小数位数, 对后面的每一个输出项都起作用
setw(n)位置输出项宽度为n位,只对后面的第一个输出项起作用
setfill(ch)设置填充字符ch, 对后面的每个输出项都起作用
setiosflags(输出格式状态)设置输出格式状态,括号中给出输出格式状态
resetionsflags(输出格式状态)终止已设置的输出格式状态

代码示例:

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
	cout << setiosflags(ios::left)             //设置左对齐
		<< setw(10)                            //设置宽度
		<< setfill('*')                        //设置填充符
		<< "左对齐" << endl                    //输出内容
		<< resetiosflags(ios::left);           //关闭左对齐方式

	cout << setiosflags(ios::right)            //设置右对齐
		<< setw(10)                            //设置宽度
		<< setfill('*')                        //设置填充符
		<< "右对齐" << endl                    //输出内容
		<< resetiosflags(ios::right);          //关闭右对齐方式

	cout << setiosflags(ios::scientific)       //设置科学计数法方式
		<< setprecision(8)                     //输出8位小数
		<< 7.0/3 << endl                       //输出内容
		<< resetiosflags(ios::scientific);     //关闭科学计数法方式

	cout << setiosflags(ios::fixed)            //设置浮点方式
		<< setprecision(8)                     //输出8位小数
		<< 7.0 / 3 << endl                     //输出内容
		<< resetiosflags(ios::fixed);          //关闭浮点方式

	cout << setprecision(8)                     //设置以一般十进制小数形式输出,输出8为数字
		<< 7.0 / 3 << endl;                     //输出内容
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
其中setw(10)对后面的第一个输出项起到作用,如果后面输出宽度也是10,则需要重新设置。
setfill(’*’)设置填充对后面所有的输出起到作用
setprecision(8)设置精度也是对后面所有输出起到作用

3.3使用输出类成员函数控制输出格式

除了可以用控制符来控制输出格式外, 还可以等价通过调用流对象cout的成员函数来控制输出格式,用于控制输出格式的常用成员函数

                   常用输出流控制格式的成员函数

成员函数等价的输出流控制符功能
precision(n)setprecision(n)设置实数精度为n位。在以一般十进制小数形式输出时n代表有效数字。在以定点格式和科学记数法格式输出时n位小数位数, 对后面的每一个输出项都起作用
width(n)setw(n)位置输出项宽度为n位,只对后面的第一个输出项起作用
fill(ch)setfill(ch)设置填充字符ch, 对后面的每个输出项都起作用
setf(输出格式状态)setiosflags(输出格式状态)设置输出格式状态,括号中给出输出格式状态
unsetf(输出格式状态)resetionsflags(输出格式状态)终止已设置的输出格式状态

代码示例:

#include <iostream>
using namespace std;
int main()
{
	cout.setf(ios::left);           //设置左对齐
	cout.width(10);                 //设置宽度为10
	cout.fill('*');                 //设置填充字符为'*'
	cout << "左对齐" << endl;       //输出内容
	cout.unsetf(ios::left);         //终止左对齐方式

	cout.setf(ios::right);          //设置右对齐
	cout.width(10);                 //设置宽度为10
	cout.fill('*');                 //设置填充字符为'*'
	cout << "右对齐" << endl;       //输出内容
	cout.unsetf(ios::right);        //终止右对齐方式

	cout.setf(ios::scientific);     //设置浮点数以科学计数法方式输出
	cout.precision(8);              //设置以科学计数法方式输出,保留小数8位
	cout << 7.0 / 3 << endl;        //输出内容
	cout.unsetf(ios::scientific);   //终止科学技术法方式

	cout.setf(ios::fixed);          //设置浮点数以定点格式输出(小数形式)
	cout.precision(8);              //输出8位有效数字
	cout << 7.0 / 3 << endl;        //输出内容
	cout.unsetf(ios::fixed);        //终止浮点数格式输出

	cout.precision(8);              //设置在以一般十进制小数位数形式输出时,输出8位有效数字
	cout << 7.0 / 3 << endl;        //输出内容
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

4.常用输入输出流成员函数

4.1输出流成员函数put()

对于输出单个字符,除了可用输出运算符"<<"外,还可以用输出流成员函数put()实现.
代码示例:

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
	char ch[30];
	int i;
	cin >> ch;
	for (i = 0; i < strlen(ch); i++)
		cout.put(ch[i]);
	cout << "\n";
	system("pause");
	return 0;
}

运行结果1:
在这里插入图片描述
运行结果2:
在这里插入图片描述

通过测试得到,该函数遇到空格就结束了,不能打印空格,遇到空格就结束后面的输出

4.2 输入流成员函数get()

成员函数get()用于输入单个字符,使用方式如下:
cin.get()
代码示例:

#include <iostream>
using namespace std;
int main()
{
	char ch;
	cout << "请输入一行字符:" << endl;
	ch = cin.get();
	while (ch != '\n')
	{
		cout.put(ch);
		ch = cin.get();
	}
	cout << '\n';
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

4.3 输入流成员函数getline()

getline()成员函数的功能是从输入流对象中输入一行字符
格式:
 输入流对象.getline(字符指针, 字符个数)
示例:

#include <iostream>
using namespace std;
int main()
{
	char ch[20];
	cin.getline(ch, 5);
	cout << ch << endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

这里设置的字符的个数是5个,abcd’\0’这5个
但其实getline有三个参数,第三个参数是结束符.
示例:

#include <iostream>
using namespace std;
int main()
{
	char ch[20];
	cin.getline(ch, 10, '*');
	cout << ch << endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
此时设置的结束符是字符号,遇到号就结束

4.4输入流成员函数eof()、peek()、putback()

eof()函数
格式:
输入流对象.eof()
功能:
 用于检测是否到达文件末尾,如果到达返回true,否则返回false。

peek()函数
格式:
输入流对象.peek()
功能:
 用于查看当前对象输入流的字符,但输入流对象的当前位置不变

putback()函数
格式:
输入流对象.putback()
功能:
 用于将一个字符插入到当前输入流对象位置上。
示例:

#include <iostream>
using namespace std;
int main()
{
    char ch1[20];
    char ch2;
    cin >> ch1;
    // cin.getline(ch1, 20, '*');
    cout << ch1 << endl;
    ch2 = cin.peek();    //返回输入流中的下一个字符
    cout << ch2 << endl;  //查看输入流中的字符
    cin.putback(ch1[0]);   //将数组的第一个元素插入到输入流中
    ch2 = cin.peek();      //查看当前输入流的数据
    cout << ch2;
    system("pause");
    return 0;
}

运行结果:
在这里插入图片描述

输入:qianfeng 回车(’\n’) 此时将qianfeng还有回车(’\n’)全部到输入缓冲区内了,然后当按下回车后
那么qianfeng全部转移到了代码关联的变量上,这里是一个ch1数组, 那么留在缓冲区的是’\n’
此时我用peek()函数监控缓冲区的数据,返回给ch2 此时输出语句是 cout << ch2 << endl;
而ch2通过缓冲区得到的是换行,然后又输出endl换行 然后通过putback(ch1[0]) 该语句是
ch1字符串第一个元素插入到缓冲区里面去 此时再监控,得到的是q, 因为字符串是qianfeng,第一个元素是q

输出结果:
qianfeng
qianfeng
回车
回车
q

5.文件的操作与文件流

文件的分类:普通文件和设备文件。
普通文件:存储在磁盘上的文件
设备文件:外部设备(鼠标、键盘)

文件编码分为:ASCII码文件和二进制文件
存放ASCII码
 例如:5 6 8 0存储形式
0011 0101   0011 0110   0011 1100   0011 0000
  5         6         8         0

二进制文件是按二进制的编码方式来存放。
例如数:5 6 8 0 存储形式
00000000   00000000      00010110   00101110

文件流实际上就是以外存文件为输入输出对象的数据流。
输出文件:从内存流向文件
输入文件:从文件流向内存

ifstream:从istream类所派生的,用于支持文件的输入操作。
ofstream:从ostream类所派生的,用于支持文件的输出操作。
fstream:从iostream类所派生的,用于支持文件的输入输出操作。

定义文件对象示例:
ofstream outFile; //定义输出文件流对象outFile
ifstream intFile; //定义输入文件流对象intFile
fstream File; //定义输入输出文件流对象File

5.1文件的打开与关闭操作

                 常用文件操作方式

文件操作方式功能
ios::in以输出方式打开文件,如果文件不存在将出错,否则打开成功,是文件流类ifstream的默认打开方式,打开后文件当前位置在文件的开始处
ios::out以输出的方式打开文件,如果文件不存在,将建立一个新文件,否则将清空文件,是文件流类ofstream的默认打开方式,打开后文件当前位置在文件的开始处
ios::app以追加方式打开,如果文件已存在,则不清除原有内容,否则编译器报错,打开后文件的当前位置在文件的末尾处
ios::binary以二进制方式打开文件,如不指定此方式,则默认为ASCII方式打开方式是

每打开文件流对象内部都有一个文件指针,用于指向当前的操作位置,每次读写操作都从文件指针指向当前位置开始。当读出一个字节,指针将向后移一个字节。
当文件指针移动到最后时候,会遇到EOF文件结束符,此时对象的成员函数eof()的值为真,表示文件结束。
 可以用位或运算符 “|” 对输入输出方式进行组合
ios::in | ios::out //以输入输出方式打开一个文本文件
ios::app | ios::binary //以追加方式打开一个二进制文件

文件打开的两种方式:
文件流对象.ope(文件名,文件操作方式)
例如

ofstream outFile;
outFile.open("1.txt", ios::out);

ofstream outFile("1.txt", ios::out);

如果文件打开失败,文件流成员函数fail()返回真,否则返回假

ofstream outFile("1.txt", ios::out);
if (outFile.fail())
{
    cout << "文件打开失败!" << endl;
}

5.2关闭文件操作

当对已打开的外存文件的操作完成后,应当关闭文件。使用成员函数close()关闭文件。
格式如下:
 文件流对象.close()
例如:

outFile.close();

关闭文件将解除外存文件与文件流对象的关系,这样将不能再通过文件流对象对该文件进行操作。

5.3文件的操作

5.3.1示例1

将含有8个整数,将这些数据存入到一个文本文件中,然后从这个文件中读数据并显示在屏幕上。
代码示例:

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	int a[] = { 1,2,3,8,2,5,6,7 };
	int n = 8;
	int k;                          //存储从文件读取出来的元素
	fstream f;                      //文件输入输出流对象f
	f.open("1.txt", ios::out);      //输出方式打开文件1.txt, 如果1.txt文件不存在,则自动新建一个1.txt文件
	if (f.fail())                   //判断打开是否成功  打开失败返回真  成功返回假
	{
		cout << "打开失败!" << endl;
		exit(1);
	}
	for (int i = 0; i < 8; i++)
		f << a[i] << " ";           //输出到文件为1.txt中
	f.close();                      //关闭文件

	f.open("1.txt", ios::in);       //以输入方式打开
	if (f.fail())                   //判断打开是否成功  打开失败返回真  成功返回假
	{
		cout << "打开失败!" << endl;
		exit(1);
	}
	while (!f.eof())                 //判断是否到达文件的末尾  到达返回真  没有到达返回假
	{
		f >> k;                      //输入到变量k中  每输出一个,则文件指针向后移动一个字节
		cout << k << " ";
	}
	cout << '\n';
	f.close();
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述

在当前文件夹下,确实自动生成了文件名为"1.txt"
该文件夹里面写入的内容也正是数组元素的值。
但是在控制台上发现却多了一个数字7
这是因为,每次输入到文件的是一个数字带一个空格
那这样来讲,当文件指针指向的是7位置时候,然后将7输入给k,然后输出7 和一个空格 ,此时文件指针后移动一位,到达文件里面数字7后面空格位置,此时while判断成立,然后将空格输入给k,却没有输入成功,k此时还是7,此时执行输出,又输出了一个7和一个空格,最终输出两个7的结果。

5.3.2示例2

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	char ch;
	int letters = 0;                             //遍历文件字母的个数
	ofstream outFile("10.txt", ios::out);       //以输出方式打开文件
	if (outFile.fail())                         //判断是否打开成功
	{
		cout << "打开失败!" << endl;
		exit(1);
	}
	ch = cin.get();
	while (ch != '\n')
	{
		outFile.put(ch);
		ch = cin.get();
	}
	outFile.close();                            //关闭文件

	ifstream inFile("10.txt", ios::in);         //输入方式打开文件
	if (inFile.fail())                         //判断是否打开成功
	{
		cout << "打开失败!" << endl;
		exit(1);
	}
	while (!inFile.eof())       //判断到没到文件末尾
	{
		ch = inFile.get();
		if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z')
		{
			cout.put(ch);
			letters++;
		}
	}
	cout << endl << "共有英文字母" << letters << "个" << endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
当前文件夹自动生成
在这里插入图片描述

5.3.3关于文件读写操作出现的乱码问题

我如果对一个已经存在的1.txt文件进行读取该文件的中文,读出来的结果确是乱码。
因为编码不同。
按win+R输入cmd
在这里插入图片描述
在这里插入图片描述
然后鼠标反键单机最上面样条框,选择属性
在这里插入图片描述
编码是GBK
而记事本的编码是UTF-8
在这里插入图片描述
如果一个1.txt记事本文件事先已经存在,此时直接读取该文件数据,如果该文件里面的数据是中文,那么读到的可能是乱码
那么解决办法:
 先对该文件执行写入的操作后,然后再在到该文件执行读取的操作,我的理解是,只有先执行写的操作,但是如果该文件不存在,就会自动创建一个.txt文件,然后才能执行读取操作后,此时读取到的文件格式相同。
注意:执行写的操作,必须将数据写进去,这才算成功,如果只是想让代码生成一个.txt文件是不行的,得先 生成.txt文件后,然后吧东西写进去才行
如果.txt文件事先已经存在先执行读的操作示例如下:
读取的操作(失败)示例:

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	char s[100];      //从文件数据读取到s数组中
	char name[100];   //文件名
	fstream file;     //输入输出流对象

	cout << "请输入要读取的文件名:";
	cin.getline(name, 100);
	
	file.open(name, ios::in);   //读取方式打开一个已经存在的文件
	if (file.fail())
	{
		cout << "打开失败" << endl;
		exit(1);
	}
	file.getline(s, 100);   //将文件的内容读取到字符数组s中去
	file.close();           //关闭文件
	cout << s << endl;      //打印读取到的东西
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
事先自己先新建了一个4.txt文件,里面的内容是:“我爱学习C++”
在这里插入图片描述
由于是我手动创建的一个4.txt文件,事先准备好的文件,然后我写一个读取4.txt文件的代码,发现读取的一部分是乱码,但是后面C++却读出来了,中文都是乱码,一个中文占2字节,一个字母占1个字节,编码方式不同,导致中文是乱码。
此时我如果先执行写入的操作之后,然后再执行读的操作,看看是不是有不一样的结果:
示例:

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	char s[100];      //从文件数据读取到s数组中
	char name[100];   //文件名
	fstream file;  //输入流对象

	cout << "请输入文件名:";
	cin.getline(name, 100);
	file.open(name, ios::out);  //写入方式打开文件
	if (file.fail())
	{
		cout << "打开失败" << endl;
		exit(1);
	}
	file << "我爱学习C++";    //吧数据写进去
	file.close();            //关闭文件
	
	//file.open(name, ios::in);   //读取方式打开文件
	//if (file.fail())
	//{
	//	cout << "打开失败" << endl;
	//	exit(1);
	//}
	//file.getline(s, 100);
	//file.close();           //关闭文件
	//cout << s << endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
此时自动生成了一个10.txt的文本文件,并且10.txt文本文件有东西写进去算成功。

在这里插入图片描述
此时将“我爱学习C++”成功写入了,这算写的操作执行完成。
接下来吧写操作的部分注释掉,执行读的操作。

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
	char s[100];      //从文件数据读取到s数组中
	char name[100];   //文件名
	fstream file;  //输入流对象

	cout << "请输入文件名:";
	cin.getline(name, 100);
	//file.open(name, ios::out);  //写入方式打开文件
	//if (file.fail())
	//{
	//	cout << "打开失败" << endl;
	//	exit(1);
	//}
	//file << "我爱学习C++";
	//file.close();            //关闭文件
	
	file.open(name, ios::in);   //读取方式打开文件
	if (file.fail())
	{
		cout << "打开失败" << endl;
		exit(1);
	}
	file.getline(s, 100);
	file.close();           //关闭文件
	cout << s << endl;
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
吧中文读取出来了,但是如果吧10.txt文件中的中文改成我想要的中文后,再执行读的操作。
在这里插入图片描述
此时10.txt里面的中文改为:"我喜欢学习,学习使我快乐"此时继续读取该文件。
运行结果:
在这里插入图片描述
成功读取。

总结:想要读取中文,首先先执行写的操作,执行写的操作时候要吧中文写入到.txt文本文件才算成功,然后才能再在该文件执行读的操作,此时该.txt文件里面的中文可以想改成什么都可以读出来。
但如果有给.txt文本文件事先已经存在(手动创建),并且里面有中文,此时如果想直接读取该存在的.txt文件,中文只能读取到乱码,除非吧文本文件编码格式改一下:

操作步骤
在这里插入图片描述

点击文件,然后点另存为
在这里插入图片描述
编码改为ANSI格式,与之前的GBK格式相同。
但是记事本有个问题,就算改了ANSI格式,之后又默认为UTF-8格式。
在这里插入图片描述

5.3.4 对二进制文件操作函数------read()与write()

read()函数是吧文件的数据读取到代码所关联的变量里面去
write()函数是吧数据写入到指定的文件去

两函数声明格式:
文件流对象.read(字符指针 buffer, 长度 len);
文件流对象.write(字符指针 buffer, 长度 len);
字符指针buffer是指向内存中一个存储空间, len是指读写的字节数
代码示例:

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
	int a[] = { 1,2,3,4,5,6 }, b;


	fstream file;                                  //输入输出流对象
	file.open("10.txt", ios::out | ios::binary);    //二进制写入方式打开1.txt文件
	if (file.fail())                               //文件失败返回真
	{
		cout << "文件创建失败!" << endl;
		exit(1);
	}
	for (int i = 0; i < 6; i++)
		file.write((char *)&a[i], sizeof(int));    //写入的每一个元素都是int字节大小
	file.close();                                  //关闭文件




	file.open("10.txt", ios::in | ios::binary);                  //二进制读取方式打开1.txt文件
	if (file.fail())                               //文件失败返回真
	{
		cout << "文件创建失败!" << endl;
		exit(1);
	}
	file.read((char *)&b, sizeof(int));             //读取一个int字节大小的数据到b中去
	while (!file.eof())                              //判断文件是否到达末尾,如果到达则返回真,否则返回假
	{
		cout << b << " " ;
		file.read((char*)&b, sizeof(int));             //读取一个int字节大小的数据到b中去
	}
	cout << endl;
	file.close();
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述
但是发现10.txt文本文件出现了乱码,因为read与write以二进制形式,所以出现乱码。
其中

for (int i = 0; i < 6; i++)
		file.write((char *)&a[i], sizeof(int));    //写入的每一个元素都是int字节大小

这两行代码可以替换成

file.write((char *)&a[0], sizeof(int));    //写入的每一个元素都是int字节大小
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值