流的概念
流就是流动,从某一处流向另一处的过程
在C++中,流的基本单位是字节,因此也称为字节流

流的四种状态

goodbit 有效状态
badbit 系统级别的错误,不可恢复
failbit 可恢复的错误
eofbit 到达了流的末尾

标准IO

标准IO四种状态和重置忽略函数的使用

io标准库,与IO常用函数

#include <iostream>

using std::cin;
using std::cout;
using std::endl;
using std::istream;

标准IO使用

#include <iostream>

using std::istream;
using std::cin;
using std::cout;
using std::endl;
using std::string;

void printStreamStatus(istream& is)//istream为自定义类型,表示输入函数
{
//输入函数cin本质上是一个对象,内部也有相应的成员函数可以调用
//good bad fail eof 与其上意义相同,为1为真,0为假
cout << "is goodbit:" << is.good() << endl;
cout << "is badbit:" << is.bad() << endl;
cout << "is failbit" << is.fail() << endl;
cout << "is eofbit" << is.eof() << endl;
}
void test0()
{
int number = -1;

printStreamStatus(cin);//去检查一下输入流当前的状态

cin>>number;
cout << "number:" << number << endl;

printStreamStatus(cin);

string line;
cin >> line;
cout << "line:" << line << endl;

printStreamStatus(cin);
}
int main()
{
test0();
return 0;
}

以上代码,当输入正确格式(先输入int类型,再输入string类型),则输出结果

is goodbit:1
is badbit:0
is failbit0
is eofbit0
123
number:123
is goodbit:1
is badbit:0
is failbit0
is eofbit0
abc
line:abc
is goodbit:1
is badbit:0
is failbit0
is eofbit0

可以看到除goodbit以外其余皆为假,则流的状态是正常的
但当格式输入有误时,比如输入了string

is goodbit:1
is badbit:0
is failbit0
is eofbit0
abc
number:0
is goodbit:0
is badbit:0
is failbit1
is eofbit0
line:
is goodbit:0
is badbit:0
is failbit1
is eofbit0

number的值因为输入有误成了0(如果是输入了比int还大的数字,会打印越界后的数字而不是0),goodbit也成了假,输入stirng的时刻直接没有执行,goodbit也为假,但我们发现failbit为真,所以在第一次输入有误时我们可以弥补错误

void test0()
{
int number = -1;

printStreamStatus(cin);//去检查一下输入流当前的状态

cin >> number;
cout << "number:" << number << endl;
printStreamStatus(cin);

//重置流的状态
cin.clear();
//清空缓冲区
cin.ignore(1024,'\n');//表示最多跳过1024个字节,直到遇到换行符


string line;
cin >> line;
cout << "line:" << line << endl;

printStreamStatus(cin);
}

此时我们发现无论第一次输入是否有误,都不会影响输入string的过程

假设我们在对cin重置后没有清空缓冲区,那么输入的数据会保留到下一次输入,也同样会取消string的输入,第一次输入的数据就是line的值

输出结果

is goodbit:1
is badbit:0
is failbit0
is eofbit0
abc
number:0
is goodbit:0
is badbit:0
is failbit1
is eofbit0
abc
line:abc
is goodbit:1
is badbit:0
is failbit0
is eofbit0

自定义安全输入

既然已经掌握了四种状态表示和如何重置流与清空缓冲区
那么可以试着写一个专门用于输入的函数

#include <iostream>

using std::istream;
using std::cin;
using std::cout;
using std::endl;
using std::string;

int readIntergerNumber()
{
int number = -1;

cout<<">:Please Input an interger number"<<endl;

while(cin>>number && !cin.eof())//直到输入结束
{
if(cin.bad())
{
cout<<">:cin stream has broken"<<endl;
return -1;
}
else if(cin.fail())
{
cout<<">:Please reInput a vaild interger number"<<endl;
cin.clear();
cin.ignore(1024,'\n');
}
else
{
cout<<">:Number:"<<number<<endl;
return number;
}
}
}
void test1()
{
readIntergerNumber();
}
int main()
{
test1();
return 0;
}

缓冲区

注:当前代码在Linux系统中执行,其他编译器可能情况有所不同

三种缓冲区

全缓冲区:只有缓冲区满时才会输出/刷新
行缓冲区:只有缓冲区满/遇到换行时才会输出/刷新
非缓冲区:不带缓冲区,有多少数据就输出/刷新多少数据,类似于word编辑

#include <iostream>

using std::istream;
using std::cin;
using std::cout;
using std::endl;
using std::string;

int main()
{
cout<<"Hello<<endl;//可以认为endl就是换行符,也就表明cout使用的就是行缓冲区

for(int i = 0;i < 512;++i)
{
cout<<'a';
}
Sleep(3000);//睡眠三秒
}

此时我们发现,当Hello打印后,过了三秒之后512个a才被打印出来
而当i循环改为1025时,可以发现打印完a之后才进行休眠
可知cout的默认缓冲区大小为1024字节

文件IO

三种输出函数

cout打印缓冲区内的数据
cerr打印错误信息
clog打印日志信息

#include <iostream>

using std::cin;
using std::cout;
using std::endl;
using std::cerr;
using std::clog;

void test0()
{
cout<<"cout"<<endl;
cerr<<"cerr""<endl;
clog<<"clog"<<endl;
}
int main()
{
test0();
}

文件打印结果

cout

错误文件打印结果

cerr
clog

当将编译执行后的打印放进文件中,会发现文件中仅有cout输出
当指定编译执行后的打印以标准错误2放进文件中,会发现文件中有cerr,clog

ifstream

用于文件的读功能,属于文件IO内的一种自定义类型

#include <iostream>
#include <fstream>
#include <string>
#include <string.h>

using std::cin;
using std::cout;
using std::endl;
using std::cerr;
using std::clog;
using std::string;
using std::ifstream;

void test1()
{
ifstream ifs("namespace.cpp");//打开当前目录下的namespace.cpp文件

if(!ifs.good())//文件不存在或其他问题
{
cout<<"ifstream open failed"<<endl;
return;
}

string word;
while(ifs>>word) //ifstream类型读入默认以空格和制表符分隔
{
cout<<word<<' ';
}

//记得关闭文件
ifs.close();
}
int main()
{
test1();
}

注:namespace.cpp是我之前已经写过的一个cpp文件,你们可以选择一个txt类型的文件打开,也是可以的

输出结果

#include <iostream> //using namespace std以后可以禁掉了 using std::cin;// :: 作用域限定符 using std::cout; //using std::endl; extern int number2;//跨模块调用 //匿名空间内定义的变量无法跨模块调用,与static相同 namespace wd { int num = 1; void print() { cout << "print()" << std::endl; } namespace lr { int num; void display() { cout << "display" << std::endl; } }//end of namespace lr }//end of namespace wd namespace//匿名空间直接使用 { int num = 2; } namespace wd2 { void print(); }//命名空间内部函数可以像函数一样声明再定义,可以多次定义命名空间 //不同的是,一般函数声明可以多次,但定义只有一次 //命名空间就像容器,可以无限定义实体 //如果函数调用的形参与之前定义的变量重名,所调用的变量的值遵循就近原则 //如果有形参,值就是形参,如果没有,就找最近的之前相应变量的值 int main() { wd::print(); cout << wd::num << std :: endl; wd::lr::display(); cout << num << std::endl; using namespace wd; print(); cout << number2 << std::endl; wd2::print(); return 0; } namespace wd2 { int abc = 1; void print() { cout << abc << std::endl; } }

可以发现,这样输出的结果不是给人看的,需要一样东西来让他能够一行一行输出出来

在之前C++入门阶段我们接触到一个函数为getline,参数只能传字符数组而不能传string对象,但这也够用

#include <iostream>
#include <fstream>
#include <string>
#include <string.h>

using std::cin;
using std::cout;
using std::endl;
using std::cerr;
using std::clog;
using std::string;
using std::ifstream;

void test1()
{
string filename("namespace.cpp");
ifstream ifs(filename);//用字符串也可以简便ifstream的传参过程

if(!ifs.good())
{
cout<<"ifstream open file failed"<<endl;
return;
}

char buff[1024] = {0};//创建buff数组存储一行数据
while(ifs.getline(buff,1000))//ifstream类中有getline成员函数,可直接调用
{
cout<<buff<<endl;
memset(buff,0,sizeof buff);
}

ifs.close();
}
int main()
{
test1();
}

输出结果

#include <iostream>

//using namespace std以后可以禁掉了
using std::cin;// :: 作用域限定符
using std::cout;
//using std::endl;

extern int number2;//跨模块调用
//匿名空间内定义的变量无法跨模块调用,与static相同

namespace wd
{
int num = 1;

void print()
{
cout << "print()" << std::endl;
}
namespace lr
{
int num;
void display()
{
cout << "display" << std::endl;
}

}//end of namespace lr
}//end of namespace wd
namespace//匿名空间直接使用
{
int num = 2;
}
namespace wd2
{
void print();
}//命名空间内部函数可以像函数一样声明再定义,可以多次定义命名空间
//不同的是,一般函数声明可以多次,但定义只有一次
//命名空间就像容器,可以无限定义实体

//如果函数调用的形参与之前定义的变量重名,所调用的变量的值遵循就近原则
//如果有形参,值就是形参,如果没有,就找最近的之前相应变量的值
int main()
{
wd::print();
cout << wd::num << std :: endl;
wd::lr::display();
cout << num << std::endl;
using namespace wd;

print();

cout << number2 << std::endl;
wd2::print();

return 0;
}
namespace wd2
{
int abc = 1;
void print()
{
cout << abc << std::endl;
}
}

此时打印的代码就非常可观了

ostream

ostream对象用于文件写入

成员函数

ofs,open(string);打开当前目录下的string文件
ofs.write(string,len);//第一个参数为要写入的字符串,第二个参数指定写入到多少长度为止
ofs.tellp();//返回当前流游标位置
ofs.seekp(n);流回到指定位置,0表示起始位置

#include <iostream>
#include <fstream>
#include <string>
#include <string.h>

using std::cin;
using std::cout;
using std::endl;
using std::cerr;
using std::clog;
using std::ifstream;
using std::ofstream;
using std::fstream;
using std::string;

void otest()
{
ofstream ofs;
ofs.open("test.txt", std::ios::out | std::ios::app);
//out为open函数默认参数,表示不要求文件存在,
//如果文件存在则进行重定向
//如果文件不存在则创建文件并写入
//app即append追加模式

if (!ofs)//简便操作,可以不用调用good
{
cout << "ofstream file open failed" << endl;
return;
}

//对文件开始写入
ofs << "this is a test" << endl;

size_t pos = ofs.tellp();

ofs.write("this is a new test\n", strlen("this is a new test\n")) << endl;

//记得关闭文件
}
int main()
{
otest();
}

fstream

fstream能进行读写操作,存在于fstream模板中
既包含ifstream的成员函数,也包含ofstream的成员函数

#include <iostream>
#include <fstream>

using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::fstream;

void ftest()
{
//fstream默认规定打开的文件必须存在,且要从文件的开头读写
fstream fs("test.txt", std::ios::in | std::ios::out | std::ios::app);

if (!fs)
{
cout << "fstream file open failed " << endl;
return;
}

int number = -1;

for (int i = 0; i < 4; ++i)
{
cin >> number;
fs << number << ' ';
}
cout << endl;

//当使用tell查找游标位置时,tellg是输入流,tellp是输出流

fs.seekp(0);

char buff[1024] = { 0 };

while (fs.getline(buff, sizeof buff))
{
cout << buff;
memset(buff, 0, sizeof buff);
}

fs.close();
}
int main()
{
ftest();
return 0;
}

字符串IO

模板库

自定义类型
istringstream将字符串转换为其他类型
ostringstream将缓冲区内数据转化为字符串
stringstream

istringstream

#include <iostream>
#incldue <sstream>

using std::cin;
using std:;cout;
using std::endl;

void test0()
{
istringstream iss("1 2 3 4 5 6");

string word;

while(iss>>word)
{
cout<<word<<endl;
//当读入到的是缓冲区中,可以以其他类型读入
}
}
int main()
{
test0();
}

ostringstream

#include <iostream>
#include <sstream>
using std::cout;
using std::endl;
using std::string;
using std::istringstream;
using std::ostringstream;
using std::stringstream;

void test1()
{
int number = -1;
int id = 101;

ostringstream oss;

oss << "number:"<<number<<' '<<"id:"<<id<<endl;

string str = oss.str();//获取oss流中字符串数据

cout<<str<<endl;
}
int main()
{
test1();
}

输出结果

number:-1 id:101