文章目录
1 流和流类库
1.1 流的概念
程序输入:将数据从输入文件传送给程序。
程序输出:将数据从程序传送给输出文件。
C++的输入与输出:
(1)标准I/O
: 对系统指定标准设备的输入和输出,即从键盘输入数据,将数据输出至显示器屏幕,称为标准的输入输出。
(2)文件I/O
:以外存磁盘文件为对象的输入和输出,即从磁盘文件输入数据,将数据输出至磁盘文件,称为文件的输入输出。
(3)串I/O
: 对内存指定空间的输入和输出,通常指定1个字符数组作为存储空间,称为字符串的输入输出。
1.2 流的类库
C++提供强大的I/O流类库,实现标准I/O
、文件I/O
和串I/O
等3种输入输出。
流的种类 | 类名 | 作用 | 声明的头文件 |
---|---|---|---|
流的顶层基类 | ios | 抽象基类 | iostream |
标准I/O | istream ostream iostream | 标准输入流和其它输入流的基类 标准输出流和其它输出流的基类 标准输入输出流和其它输入输出流的基类 | iostream |
文件I/O | ifstream ofstream fstream | 输入文件流类 输出文件流类 输入输出文件流类 | fstream |
串I/O | istrstream ostrstream strstream | 输入字符串流类 输出字符串流类 输入输出字符串流类 | strstream |
C++ I/O流类的层次结构:
注:
iostream类
分别继承istream类
和ostream类
,构成菱形/钻石继承关系。
通过虚继承(虚基类指针vbptr
、虚基类表vbtable
)解决菱形继承中的二义性。
2 标准输入流(istream)
标准输入流对象cin
的成员函数:
(1)get()
:每次只从输入缓冲区读取单个字符(包括换行符)。
函数原型:int get();
(2)get(1个参数)
:每次只从输入缓冲区读取单个字符(包括换行符)。
函数原型:istream& get(char &ch);
参数:
ch
:从输入缓冲区读取1个字符后,用于存储的字符变量。
注:
get()
和get(1个参数)
功能完全相同,仅调用方式不同。
(3)get(2个参数)
:从输入缓冲区读取1行(字符串),并存储至字符数组中,且不清空输入缓冲区。【遗留Enter的换行符】
函数原型:istream& get(char* buffer, streamsize count);
参数:
①buffer
:从输入缓冲区读取字符串后,用于存储的字符数组;
②count
:字符数组的最大长度。
注:从输入缓冲区读取的字符串存入buffer字符数组中,但换行符
\n
仍遗留在输入缓冲区中。
解决方法:可使用cin.get(ch)
读取输入缓冲区中遗留的换行符。
例:
char buffer[1024];
cin.get(buffer, sizeof(buffer));
(4)getline(2个参数)
:从输入缓冲区读取1行(字符串),并存储至字符数组中,且清空输入缓冲区。【自动舍弃Enter的换行符】
函数原型:istream& getline(char* buffer, streamsize count);
参数:
①buffer
:从输入缓冲区读取字符串后,用于存储的字符数组;
②count
:字符数组的最大长度。
注:从输入缓冲区读取的字符串存入buffer字符数组中,且换行符
\n
不会遗留在输入缓冲区中,被自动舍弃。
例:
char buffer[1024];
cin.getline(buffer, sizeof(buffer));
(5)ignore()
:忽略输入缓冲区的1个字符。
ignore(int n)
:忽略输入缓冲区的多个字符。
(6)peek()
:从输入缓冲区观测并返回1个字符,不从输入缓冲区取走字符。
(7)putback()
:将字符放回输入缓冲区的起始位置。
示例:输入流对象cin的成员函数
#include <iostream>
using namespace std;
//get():每次只从输入缓冲区读取单个字符(包括换行符)
void func1() {
//键盘依次输入:qw(Enter回车)
//控制台打印:(1)ch = q (2)ch = w (3)ch = (4)阻塞:等待下一次输入
char ch;
ch = cin.get();
cout << "ch = " << ch << endl;
ch = cin.get();
cout << "ch = " << ch << endl;
ch = cin.get();
cout << "ch = " << ch << endl;
ch = cin.get();
cout << "ch = " << ch << endl;
}
//get(1个参数):每次只从输入缓冲区读取单个字符(包括换行符)
//`get()`和`get(1个参数)`功能完全相同,仅调用方式不同。
void func2() {
//键盘依次输入:qw(Enter回车)
//控制台打印:(1)ch = q (2)ch = w (3)ch = (4)阻塞:等待下一次输入
char ch;
cin.get(ch);
cout << "ch = " << ch << endl;
cin.get(ch);
cout << "ch = " << ch << endl;
cin.get(ch);
cout << "ch = " << ch << endl;
cin.get(ch);
cout << "ch = " << ch << endl;
}
//get(2个参数):从输入缓冲区读取1行(字符串),并存储至字符数组中,且不清空输入缓冲区。【遗留Enter的换行符】
void func3() {
//初始化字符数组
char buffer[1024] = { 0 };
cin.get(buffer, sizeof(buffer)); //换行符会遗留在缓冲区
cout << "buffer = " << buffer << endl;
char ch;
ch = cin.get(); //取走输入缓冲区的当前字符
if (ch == '\n') {
cout << "Enter的换行符遗留在输入缓冲区" << endl;
}
else {
cout << "Enter的换行符未遗留在输入缓冲区" << endl;
}
//键盘输入:hello
//控制台打印:
//buffer = hello
//Enter的换行符遗留在输入缓冲区
}
//getline(2个参数):从输入缓冲区读取1行(字符串),并存储至字符数组中,且清空输入缓冲区。【自动舍弃Enter的换行符】
void func4() {
//初始化字符数组
char buffer[1024] = { 0 };
cin.getline(buffer, sizeof(buffer)); //换行符不会遗留在缓冲区
cout << "buffer = " << buffer << endl;
char ch;
ch = cin.get(); //取走输入缓冲区的当前字符
if (ch == '\n') {
cout << "Enter的换行符遗留在输入缓冲区" << endl;
}
else {
cout << "Enter的换行符未遗留在输入缓冲区" << endl;
}
//键盘输入:hello
//控制台打印:
//buffer = hello
//(阻塞:等待下一次输入)
//Enter的换行符未遗留在输入缓冲区
}
//ignore():忽略输入缓冲区的1个字符。
//ignore(int n):忽略输入缓冲区的n个字符。
void func5() {
//键盘输入:qwer
//控制台打印:ch = r
cin.ignore(3); //忽略输入缓冲区的3个字符
char ch = cin.get();
cout << "ch = " << ch << endl;
}
//peek():从输入缓冲区观测并返回1个字符,不从输入缓冲区取走字符。
void func6() {
//键盘依次输入:qwe
//控制台打印:(1)ch = q (2)ch = q (3)ch = w
char ch = cin.peek(); //观测1个字符
cout << "ch = " << ch << endl;
ch = cin.get();
cout << "ch = " << ch << endl;
ch = cin.get();
cout << "ch = " << ch << endl;
}
//putback():将字符放回输入缓冲区的起始位置。
void func7() {
cin.putback('x'); //将字符放回输入缓冲区的起始位置
cin.putback('y'); //将字符放回输入缓冲区的起始位置
char buffer[1024] = { 0 };
cin.getline(buffer, sizeof(buffer));
cout << "buffer = " << buffer << endl;
//键盘输入:hello
//控制台打印:buffer = yxhello //依次将字符x、y放回输入缓冲区的起始位置
}
void main() {
func7();
}
3 标准输入流案例练习
案例:输入1~10之间的整数,若输入有误则重新输入。
思路:
(1)while(true)
死循环实现重复输入;
(2)使用整型变量接收键盘输入时,若键入非数字时,会导致程序死循环(string型无法转换为int型),即输入缓冲区异常(标志位异常),需要重置标志位和刷新缓冲区。
标准输入流对象的成员函数:
①fail()
:查看输入缓冲区的标志位,0表示正常,1表示异常。
②clear()
:重置输入缓冲区的标志位为正常状态(0)。
③sync()
:使输入流与对应数据源同步,丢弃之前未处理的数据,刷新并清空输入缓冲区。
注:成员函数
sync()
在Visual Studio 2019中无效,可使用getline()
函数手动取走输入缓冲区的内容。
示例:
#include <iostream>
using namespace std;
#include <string>
void func(){
cout << "请输入1~10的整数:" << endl;
while (true) {
int num;
//当键入string类型时,程序进入死循环(string类型无法转换为int类型)
cin >> num;
if (num >= 1 && num <= 10) {
cout << "输入正确,整数为:" << num << endl;
break; //退出死循环
}
cout << "输入有误,请重新输入!" << endl;
//查看输入缓冲区的标志位(0正常,1异常)
cout << "输入缓冲区的标志位:" << cin.fail() << endl;
//1.重置输入缓冲区的标志位为0(正常)
cin.clear();
//2.刷新并清空输入缓冲区
//cin.sync(); //Visual Studio 2019中无效
//使用cin.getline()手动取走输入缓冲区的内容
char buffer[1024] = { 0 };
cin.getline(buffer, sizeof(buffer));
}
}
void main() {
func();
}
4 标准输出流(ostream)
4.1 标准输出流对象cout的成员函数
标准输出流对象cout
的成员函数,但通常配合左移运算符<<
进行输出。
(1)flush()
:刷新输出缓冲区,仅对Linux系统有效。
(2)put()
:向输出缓冲区中写入单个字符,支持链式编程。
函数原型:ostream& ostream::put(char ch)
(3)write()
:向输出缓冲区中写入多个字符。
函数原型:ostream& ostream::write(const char *buffer, streamsize count)
参数:
buffer
:向输出缓冲区写入的字符数组。
count
:向输出缓冲区写入的字符数组的字符个数。
示例:
#include <iostream>
using namespace std;
//put():向输出缓冲区中写入单个字符,支持链式编程
void func1() {
cout.put('h').put('e').put('l').put('l').put('o'); //hello
}
//write():向输出缓冲区中写入多个字符
void func2() {
char buffer[1024] = "hello world";
//cout.write(buffer, 3); //hel
cout.write(buffer, strlen(buffer)); //hello world
}
void main() {
func2();
}
4.2 格式化输出
格式化输出:使数据按指定格式输出数据。通常情况下,由系统根据数据类型采用默认格式进行输出。
C++控制输出格式的2种方式:
(1)使用cout
流对象的成员函数,控制输出格式。
示例:
/* 控制输出格式:使用流对象的成员函数 */
void func() {
int num = 99;
//设置宽度
cout.width(5); // 99
//设置填充字符
cout.fill('*'); //***99
//设置左对齐
cout.setf(ios::left); //99***
//取消十进制
cout.unsetf(ios::dec); //99***
//设置十六进制
cout.setf(ios::hex); //63***
//设置显式进制的基数
cout.setf(ios::showbase); //0x63*
//取消十六进制
cout.unsetf(ios::hex); //99***
//设置八进制
cout.setf(ios::oct); //0143*
cout << num << endl;
}
(2)使用控制符,控制输出格式,需包含头文件#include <iomanip>
。
示例:
/* 控制输出格式:使用控制符 */
//需包含头文件<iomanip>
void func() {
int num = 99;
cout << setw(5) //设置宽度 // 99
<< setfill('*') //设置填充 //***99
<< setiosflags(ios::showbase) //设置显式进制的基数 //***99
<< setiosflags(ios::left) //设置左对齐 //99***
<< hex //设置十六进制 //0x63*
<< num << endl;
}