输入:将数据放到程序(内存)中
输出:将数据从程序(内存)放到设备中
C++的输入输出分为3种形式:
从键盘屏幕中输入输出,称为标准IO
对于磁盘进行标准输入输出,称为文件IO
对于指定的空间进行输入输出字符串,称为串IO
cin是通用输入流的对象。
标准IO没有进行特别的规定,比较特殊和常用没有定义额外的类。
其他的输入输出流有专门定义了的类。
通用输入输出包含文件标准字符串输入输出
流的四种状态
badbit:系统错误,置位时流无法使用
failbit:可恢复的错误,想读一个int但读成了char,修改后流正常使用
eofbit:到达文件末尾,置位
goodbit:表示流是一个有效状态
当处于前三种状态时无法正常使用,但是重点关注第二种
标准输入输出
标准输入输出是在iostream这个类中,cin其实是其中的一个对象
在iostream这个类中有成员函数good()。。。
clear()调整为流的状态为goodbit,但是因为输入是字符串但是读入进行存储的地方是int类型,所以没有能够清空这个输入的缓冲区,所以需要将缓冲区进行清空。所以说clear调整流的状态需要和ignore函数结合使用,ignore默认是忽略1个字符,不要忘记忽略最后的换行符,所以说需要设置足够的可以忽略的字符数ignore(4)用来忽略you\n。这样很麻烦不确定会输入什么。
可以通过补充功能实现cin.ignore(12,'\n');
这样最多释放12个字符,不管到不到最大值字符数,直到分隔符都忽略。
所以可以设置为cin.ignore(1024,'\n');所以前面参数可以尽可能设置更大。
如果觉得还不够大使用
#include <limits>头文件中的最大数值
input.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
//记住这个前面是一个类,其中是定义的静态函数,
使用这个定义的最大的数值
下面的这个情景:当输入错误的时候,要求继续进行输入
cpp
int x;
cout << "输入:" << endl;
//,表达式依据最后的表达式,cin会执行,但是不会让while直接结束
int number = 0;
while(cin << x,++number,!cin.eof()){
if(number > 5){
cout << "输错超过5次" << endl;
break;
}
if(cin.bad()){
cout << "不可补救" << endl;
return 0;
}else if(cin.fail()){
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
cout << "输入正确的格式" << endl;
}else{
//正确
cout << "right" << endl;
break;
}
}
//asdf3后面的数字被认为是字符串的一部分,是错误的
//2sadf后面的字母并不会被读出来,留在缓冲区中,这个输入是正确的
对于流的状态进行判断的时候可以直接判断
if(cin.good()){}
if(cin){}
是同样的效果,都可以继续进行任务。
cpp
//所以说可以这样来使用
if(cin >> num){
cout << "right !" << endl;
}
连续cin或者是cout
cpp
int a,b;
cin >> a >> b;
(cin >> a )>> b;
//两个是一样的效果
//cin语句表达式的返回值就是cin或者cout对象
如果说a输入格式不对的话,b就不会再允许进行输入
连续的输入可以通过换行来实现也可以通过空格来实现
缓冲机制
(内存)用于缓解cpu(程序)和磁盘(IO操作)的速度的差距
输入内容存在流缓冲区中,特定情况下释放
缓冲机制:
三种缓冲机制:全缓冲(填满缓冲区),行缓冲(填满一行),不缓冲
cin:全缓冲和行缓冲(换行的时候就将数据放在对应的变量)
cout:满缓冲
通过下面的例子理解:会先打印1024个a,2秒以后又打印一个a。
实际上如果是打印1500,也是会先打印1024个,然后正序结束的时候剩下的才打印。
gcc默认的缓冲区大小是1024,缓冲区满了以后先打印出来然后剩下的放在缓冲区中。
如果遇到endl就会直接刷新打印出来,或者是fflush。(endl底层实际上是刷新+换行)
1024刚好放下不会打印,2秒后打印。
无缓冲:不管来啥直接就打印出来
cerr打印错误的时候,不会使用到缓冲区,直接就打印出来,因为比较紧急。
文件输入和输出流
cpp
ifstream();
explicit ifstream(const char* filename, openmode mode = ios_base::in);
explicit ifstream(const string & filename, openmode mode = ios_base::in);
ofstream();
explicit ofstream(const char* filename, openmode mode = ios_base::out);
explicit ofstream(const string & filename, openmode mode = ios_base::out);
fstream();
explicit fstream(const char* filename, openmode mode = ios_base::in|out);
explicit fstream(const string & filename, openmode mode = ios_base::in|out);
还得需要注意哪个是输入,哪个是输出。外存文件到程序是输入,就像是cin从将键盘到内存程序
【补充】Point pt = 1; pt.print(); 这是一种隐式转换,pt就是(1,0)。但是这种格式非常的奇特,所以说采用explicit禁止这种情况
String str = "hello";
其实这也是一个隐式转换,这种隐式转换就比较正常,所以说不使用explict禁止
文件输入流
cpp
#include <fstream>
void test0(){
ifstream ifs;
ifs.open("test1.cc");
ifstream ifs2("test2.cc");
string filename = "test3.cc";
ifstream ifs3(filename);
}
//第二个参数有默认值
无参构造的时候借助于open将创建的文件输入流与文件进行绑定,文件不存在的时候流的状态变为failbit状态,这个时候添加一个判断语句。
cpp
ifstream ifs;
if(!ifs){
cout << "file is not exist! " << endl;
return;
}
通过上一部分可以知道可以在一开始的时候就和文件进行绑定,也可以一开始使用无参构造,然后
使用open函数与文件进行绑定。
cpp
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ifstream ifs;
ifs.open("string.cc");
if(!ifs){
cerr << "file error" << endl;
return ;
}
//1.c风格字符串不常用
char buf[100];
while(ifs.getline(buf, sizeof(buf))){
cout << buf << endl;
memset(buf, 0, sizeof(buf));
//c风格字符串记得清0不像c++可以自己处理空间且不用清理
}
//2.C++风格
string line;
while(getline(ifs,line)){
cout << line << endl;
}
cout << endl;
return 0;
}
第二种c++风格中的getline函数是用到istream中的getline成员函数,ifstream是istream的派生类。
读取指定字节数的内容
read函数 + seekg函数 + tellg函数
seekg用来设置游标的位置,tellg用来获取游标的位置
seekg有两种方式可以进行位置的获取,一种是绝对位置的设置另外一种是相对位置的设置方式
(1)seekg(30)偏移到第30个位置 (2)seekg(0, std::ios::end)
打开的时候就立即让游标移到末尾的位置
重要读取配置文件的时候会使用到,文件的io
文件输出流
cpp
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ofstream ofs("1.txt", std::ios::app);
if(!ofs){
cerr << "file error " << endl;
return 0;
}
string str("hello");
ofs << str;
ofs.close();
return 0;
}
打开ofstream的时候使用参数app的方式打开的话,就会追加的方式打开。
下面这种方式就是可以在另外的一个窗口中动态的查看文件的内容。
【注意】文件不存在时候不会报错,会创建文件,不像读入文件一样
字符串输入输出流
字符串输入流
cpp
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
string str = "22313 12";
int num1, num2;
istringstream iss(str);
iss >> num1 >> num2;
cout << num1 << " " << num2 << endl;
return 0;
}
这样num1和num2就被赋初值。
这样就非常方便去读配置文件
【注意】注意一个一个小细节就是使用const引用,可以避免修改和复制以及可能会绑定右值问题
cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
ifstream ifs("my.conf");
if(!ifs){
cerr << "file error" << endl;
return 0;
}
string line;
while(getline(ifs, line)){
istringstream iss(line);
string temp, use;
iss >> temp >> use;
cout << temp << ": " << use;
/* iss.close(); */
}
ifs.close();
return 0;
}
读取配置文件的过程:
通过ifstream打开文件,一次读取出来一行。
通过istringstream将一行赋给字符串输入流,实际上就是建立一个缓冲区,然后自己通过空格挨个读取出来
【注意】这个地方其实就是实现了类型的转换从string类型转换为int类型
【补充】
cout cin cerr都是对象
endl是inline函数
std::string std::isatream 是一个类
字符串输出流
cpp
void test0(){
int num = 123, num2 = 456;
ostringstream oss;
//把所有的内容都传给了字符串输出流对象
oss << "num = " << num << " , num2 = " << num2 << endl;
cout << oss.str() << endl;
}
就是将一长串任何类型的数据转换为一个string类型的数据
string可以直接用+,也可以用append,但是这两种不能跨类型实现不能string str = "hello" + 2;
可以通过std::to_string(1)变为string类型的数据。