1. IO库的组成部分
IO就是input,output的缩写,也就是输入输出功能。
- C++定义了ios这个基类来定义输入输出功能,而C++IO库的所有的类都继承自这个类。
- istream,ostream直接继承自ios类:
(1)ostream类定义了从内存到输出设备的功能,我们常用的cout就是ostream类的对象。
(2)istream类定义了从输入设备到内存的功能,我们常用的cin就是istream类的对象。
(3)iostream文件定义了ostream和istream类的对象,也就是cout和cin,我们平时引用头文件就是在做这个操作。 - ifstream,ofstream类分别继承自istream类和ostream类:
(1)ifstream类定义了从磁盘写入内存的功能,因为istream重载了<<运算符,所以ifstream也继承了<<的重载。注意:除了=运算符,其他运算符都是可以被继承的。
(2)ofstream类定义了从内存写入磁盘的功能。
(3)fstream文件引入了ifstream和ofstream,所以我们引入fstream文件就能进行文件流功能。
注意:磁盘与输入输出设备的交互只能通过内存来中转。
- istringstream,ostringstream类分别继承自istream类和ostream类:
(1)istringstream类定义了从指定字符串到特定内存的功能。
(2)ostringstream类定义了从特定内存到指定字符串的功能。
(3)sstream文件引入了istringstream和ostringstream,我们可以使用字符串与内存直接交互数据的功能。
因此我们主要引入iostream,fstream,sstream头文件进行操作。
2. IO库的注意事项
IO对象无法使用拷贝构造和赋值运算符,所以我们使用流对象无法使用值传递,一般使用引用进行传递。
#include<iostream>
#include<fstream>
#include<sstream>
void testStreamMain()
{
std::istream myCin(std::cin); // 错误,无法进行拷贝构造
}
class Test
{
friend std::ostream& operator<< (std::ostream& os, const Test& test); // 我们只能使用引用传递
};
IO对象的状态
- IO操作是非常容易出现错误的操作的,比如我们使用cin向int对象中输入一个字符串,会使cin这个对象出现错误。所以当我们使用IO对象时都应该判断IO对象的状态。
- IO库定义了iostate类型,可以完整的表示io对象当前的状态,使用不同位置1来标记错误。即使用bad()和fail()来检测出错误就可以了。
- iostate状态的类型
- badbit状态:系统级错误,一旦badbit置1,则流对象无法使用,bad()被置1返回true。
- failbit状态:可恢复的错误,当badbit置1时,failbit也会被置1,fail()被置1返回true。
- eofbit状态:当到达文件结束位置时,eofbit和failbit都会被置1。eof()被置1返回true。
- goodbit状态:表示流对象没有任何错误,good()在所有位都未被置1返回true。
- 流对象的管理
- rdstate()函数:返回一个iostate值,对应当前流状态。
- setstate(flag)函数:将对象流设置为想要的状态。
- clear()函数:将所有状态位置0。
- ignore()函数:提取字符串并丢弃。
/*
* 标准的IO书写格式
**/
#include<iostream>
void testStreamMain()
{
int i = 10;
while (std::cin >> i, !std::cin.eof())
{
if (std::cin.bad()) // 是否出现系统级错误
{
throw std::runtime_error("cin is corrupted");
}
if (std::cin.fail()) // 是否出现可恢复错误
{
// 清空状态位
std::cin.clear();
// 清空缓冲区,清空字符串,直到'\n'为止
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "data format error, please try again" << std::endl;
continue;
}
std::cout << i << std::endl;
}
std::cout << "process complete" << std::endl;
}
3. iostream
3.1 geline
getline按行接收数据,因为存储在string对象中,所以不容易出现格式错误,当仍可能出现系统级错误。
#include<string>
std::getline(cin, inputLine); // 第一个参数为cin,就是正在读取的输入流,第二个参数为inputLine是接收流读取内容的变量
注意:使用getline函数一定要包含string头文件。
#include<iostream>
#include<string>
void testStreamMain()
{
std::string str;
while (std::getline(std::cin, str), !std::cin.eof())
{
if (std::cin.bad()) // 是否出现系统级错误
{
throw std::runtime_error("cin is corrupted");
}
std::cout << str << std::endl;
}
std::cout << "process complete" << std::endl;
}
3.2 get
#include<iostream>
std::cin.get(c); // get字符并存入c中
std::cin.getline(str, count); // getline一行20个字符,存入str中,以'/n'终止
get是以字符的格式进行接收,与getline类似。
#include<iostream>
void testStreamMain()
{
char c;
while (std::cin.get(c), !std::cin.eof())
{
if (std::cin.bad()) // 是否出现系统级错误
{
throw std::runtime_error("cin is corrupted");
}
std::cout << c << std::endl;
}
std::cout << "process complete" << std::endl;
}
4. fstream
IO库没有默认给出ifstream和ofstream类的对象,因此需要我们自己定义。
#include<iostream>
#include<string>
#include<fstream>
/*
* 标准的文件流书写格式
* 功能:输入文件名,打开相应文件,并输出文件的内容
**/
void testStreamMain()
{
std::string fileName;
std::string fileContent;
while (std::cin >> fileName, !std::cin.eof())
{
if (std::cin.bad()) // string存储,只需要判断bad()
{
throw std::runtime_error("cin is corrupted");
}
std::ifstream ifs(fileName); // 定义ifstream,一个文件流对象,一定要绑定一个文件
if (ifs.is_open())
{
while (std::getline(ifs, fileContent))
{
std::cout << fileContent << std::endl;
}
if (ifs.bad())
{
std::cout << "ifs is corrupted" << std::endl;
}
ifs.close(); // 一定要关闭!因为文件存在操作系统内核区,打开了不关闭很麻烦!
}
else
{
ifs.clear();
ifs.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "file not exist, please try again" << std::endl;
continue; // 循环继续
}
}
std::cout << "process complete" << std::endl;
}
文件模式:
(1)in:以读的方式打开,ifstream的默认模式。
(2)out:以写的方式打开,ofstream的默认模式。
(3)app:在进行写操作时定位到文件末尾。
(4)ate:打开文件后定位到文件末尾。
(5)trunc:截断文件(把文件已有的全部删除,重新开始写),ofstream的默认写文件方式。
(6)binary:以二进制的方式打开文件。
std::ofstream ofs("test.txt", std::ios::app | std::ios::trunc); // 追加模式与截断模式,注意文件模式也是按位操作
if (ofs.is_open())
{
ofs << "Hello" << std::endl;
ofs.close();
}
5. sstream
sstream流可以向string对象写入数据,也可以从string对象读取数据,与文件操作类似,string流默认包含一个string对象。
- 对于stringstream,也需要我们自己定义流对象。
- 构造函数指定对应的string,如果是默认的构造函数,是一个空的字符串。
- str()函数:返回流对应的字符串的值(不是引用)。
- str(“XXX”)函数:表示原有的字符串被销毁,存储新的“XXX”,返回void。
void testStreamMain()
{
std::string str("Hello");
std::istringstream isstream(str);
isstream.str();
}
- string流的类型转化
string流进行string与其他类型的转化,这也是string流对象最重要的功能。
#include<iostream>
#include<string>
#include<sstream>
/*
* 功能:通过流将string转换为int
**/
void testStringToInt()
{
std::string str("12");
std::stringstream strStream(str);
int i = 0;
strStream >> i; // 直接将string流对象流出,完成格式转化
if (strStream.bad())
{
throw std::runtime_error("strStream is corrupted");
}
else if (strStream.fail())
{
strStream.clear();
strStream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "format error" << std::endl;
}
else
{
std::cout << i << std::endl;
}
}
/*
* 功能:通过流将int转换为string
**/
void testIntToString()
{
int i = 50;
std::stringstream strStream;
strStream << i << std::endl; // 直接将string流对象流入,完成格式转化
if (strStream.bad())
{
throw std::runtime_error("strStream is corrupted");
}
else
{
std::cout << strStream.str() << std::endl;
}
}
- 使用string流对空格分割的字符串进行切分。
void testStreamMain()
{
std::string str("Hello Hello Hello");
std::string outStr;
std::stringstream strStream(str);
while (strStream >> outStr)
{
std::cout << outStr << std::endl;
}
if (strStream.bad())
{
throw std::runtime_error("strStream is corrupted");
}
}