览
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字节大小