最近在忙一些事情,挂科了还要准备开学补考呜呜,过几天更新
文章目录
前言
本文是完全写自己看的《C++ primer》笔记,内容写的很快,所以可能有较多遗漏,这是第II部分,第一部分请移步我的C++ primer笔记(第I部分)
第II部分 C++标准库
第八章 IO库
C++不直接处理输入输出,而是通过一族定义在标准库中的类型来处理IO。这些类型支持从设备读取数据、向设备写入数据的IO操作,设备可以是文件、控制台窗口等。还有一些类型允许内存IO,即,从string读取数据,向string写入数据。
8.1 IO类
为了支持使用宽字符的语言,标准库定义了一组类型和对象来操纵wchar_t
类型的数据。宽字符版本的类型和函数的名字以一个w
开始。例如wcin、wcout、wcerr
是分别对应cin、cout、cerr
的宽字符版本对象。宽字符版本的类型和对象与其对应普通的char
版本的类型定义在同一个头文件中。例如,头文件fstream
定义了ifstream
和wifstream
类型。
IO类型间的关系
标准库使我们能忽略不同类型的流之间的差异,这是通过继承机制实现的。利用模板,我们可以使用具有继承关系的类,而不必了解继承机制如何工作的细节。
简单的说,继承机制使我们可以声明一个特定的类继承自另一个类。我们通常可以将一个派生类(继承类)对象当做其基类(所继承的类)对象来使用。
IO对象无拷贝或赋值
我们不能拷贝或对IO对象赋值。
ofstream out1, out2;
out1 = out2; //错误,不能对流对象赋值
ofstream print(ofstream); //错误,不能初始化ofstream参数
out2 = print(out2); //错误,不能拷贝流对象
由于不能拷贝IO对象,所以我们也不能将形参或返回值设置为流类型。通常使用引用方式传递,读写一个IO对象会改变其状态,因此传递和返回的引用是不能const的。
条件状态
一个错误的IO例子
int val;
while(cin >> val) cout << 1 << '\n';
一旦我们输入一个字母A,读操作就会失败,代码期望得到一个int,却得到了一个字符A。这样cin会进入错误状态,终止读入。(文件结束标志是一样的效果)
流状态的查询和控制
IO库定义了一个与机器无关的iostate
类型,它提供了表达流状态的完整功能。
流状态由eof, bad, fail, good指示,如果eof, bad, fail任何一个为true那么流监测状态为错误状态;相反都为false,则good为true.
其中badbit标志着系统级的故障,是不可恢复的,如果流出现这种情况,流通常就不能继续使用了;failbit标志着是可恢复的,可以修正。eofbit在遇到文件结束符时设置,此时还设置了failbit.
clear()和setstate()用于改变条件成员的状态。clear()将条件重设为有效状态(eof, bad, fail置为false;good置为true),setstate()把某个特定的流设为有效状态。
#include<bits/stdc++.h>
using namespace std;
istream &process_input(istream &cin) {
int x;
cin >> x;
cout << cin.good() << '\n'; //当x不为数字是输出为0
}
int main() {
auto old_state = cin.rdstate();
cin.clear();
cin.ignore(); //clear()只会把cin的状态调整为正确的状态,错误的输入还在缓冲区,
//cin.ignore()意为舍弃掉缓冲区中第一个字符或回车之前的数据
process_input(cin);
cin.setstate(old_state);
cin.clear();
cin.ignore();
cout << cin.good() << '\n'; //clear之后输出为1
return 0;
}
#include<bits/stdc++.h>
using namespace std;
istream &process_input(istream &cin) {
int x;
cin >> x;
}
int main() {
auto old_state = cin.rdstate(); //记录cin当前状态
cout << "---" << old_state << '\n'; //输出当前状态,因为状态正常结果是0
process_input(cin); //在这里输入一个B,因为读入fail
//所以cin.rdstate()为failbit,也就是4
auto bad_state = cin.rdstate();
cout << "---" << bad_state << '\n'; //输出4
cin.clear(); //clear()之后状态正确输出0
cout << "---" << cin.rdstate() << '\n';
cin.setstate(bad_state); //setstate不是覆盖原始状态,而是叠加到原始状态上,所以输出4
cout << "---" << cin.rdstate() << '\n';
cin.setstate(old_state); //setstate不是覆盖原始状态,而是叠加到原始状态上,所以输出4
cout << "---" << cin.rdstate() << '\n';
return 0;
}
管理输出缓冲
刷新输入缓冲区的五种情况:
- 程序正常结束
- 缓冲区已经满了
- endl显式刷新
- 用 unitbuf 操纵符设置流的状态,清空缓冲区
- 将输入输出流关联起来
主动刷新缓冲区的方式
cout << "1" << endl; //输出一个换行然后刷新缓冲区
cout << "1" << flush; //什么都不输出刷新缓冲区
cout << "1" << ends; //输出一个空字符刷新缓冲区
cout << unitbuf; //所有输出操作后都会立即刷新缓冲区
cout << nounitbuf; //回到正常的缓冲方式
关联输入和输出流
当一个输入流被关联到一个输出流时,任何从输入流读取数据的操作都会先刷新关联的输出流。
cin.tie(&cout); ostream *old_tie = cin.tie(); cin.tie(0); cin.tie(&cerr); cin.tie(0);
cin.tie(old_tie);
8.2 文件输入输出
文件操作
C++中对文件操作需要包含头文件<fstream>
文件分为两种:
- 文本文件:文件以文本的ASCII码形式存储在计算机中
- 二进制文件:文件以文本的二进制形式存储在计算机中
操作文件三大类:
- ofstream:写操作
- ifstream:读操作
- fstream:读写操作
文本文件
写文件
- 头文件
include<fstream>
- 创建流对象
ofstream ofs
- 打开文件
ofs.open("文件路径",打开方式)
- 写数据
ofs << "写入的数据"
- 关闭文件
ofs.close()
文件打开方式:
ios::in
为读文件而打开文件ios::out
为写文件而打开文件ios::ate
初始位置:文件尾ios::app
追加方式写文件ios::trunc
如果文件存在先删除再创建ios::binary
二进制方式
注意:文件打开方式可以配合使用,利用|
操作符
例如:用二进制方式写文件:ios::binary | ios::out
读文件
- 头文件
include<fstream>
- 创建流对象
ifstream ifs
- 打开文件
ifs.open("文件路径",打开方式)
- 读数据:四种方式
ifs >> x
每次能读取一行数据,x是字符数组或string类型ifs.getline(x, size)
x是字符数组,size是一行最多读入的字符个数getline(ifs, s)
s是string类型c = ifs.get()
读入一个字符
- 关闭文件
ifs.close()
二进制文件
打开方式要指定为ios::binary
写文件
二进制方式写文件主要利用流对象调用成员函数write
函数原型:ostream& write(const char * buffer, int len)
buffer指向内存中一段存储空间,len是读写的字节数
读文件
二进制方式读文件主要利用流对象调用成员函数read
函数原型:istream& read(char * buffer, int len)
buffer指向内存中一段存储空间,len是读写的字节数
8.3 string流
istringstream从string读数据,ostringstream向string写如数据,stringstream可读可写
使用istringstream
string line, word;
while(getline(cin, line)) {
istringstream record(line);
while(record >> word) cout << word << '\n';
}
使用ostringstream
string line, word;
ostringstream s;
while(cin >> word) {
s << " " << word;
}
cout << s.str() << '\n';
第九章 顺序容器
9.1 顺序容器概述
vector(可变大小数组)、deque(双端队列)、list(双向链表)、forward_list(单向链表)、array(固定大小数组)、string(保存字符串,与vector类似)
9.2 容器库概览
如果我们创建的类型没有默认构造函数,这时我们必须传递给它一个元素初始化器
假定T是一个没有默认构造函数的类型
vector<T> v1(10, init); //正确,提供了元素初始化器
vector<T> v2(10); //错误
begin()和end()可以改变元素值,cbegin()和cend()则不可以改变
标准库array具有固定大小
array<int, 42> a; //42个int
array<int, 42> :: size_type sz; //正确
arrat<int>:: size_type j; //错误,array<int>不是一个类型
使用assign(仅顺序容器)
赋值运算符要求左右两遍对象拥有相同的类型,assign允许我们从一个不同但相容的类型赋值。
list<string> names;
vector<const char*> oldstyle;
name = oldstyle; //错误,类型不匹配
//正确,可以将const char* 转换为string
name.assign(oldstyle.begin(), oldstyle.end());
list<string> s(1); //1个元素,空string
s.assign(10, "Hi"); //10个元素,每个都是"Hi"