文章目录
- [一、 总述](#一、 总述)
- 二、输出缓冲
- 三、文件输入输出
- 四、string流
- 五、输入输出格式
-
- 总述
- 1、控制布尔值的格式
- 2、指定整型值的进制
- 3、在输出中指出进制
- 4、控制浮点数格式
-
- [4.1、 指定打印精度](#4.1、 指定打印精度)
- 5、输出空白
- 六、未格式化的输入输出操作
一、 总述
C++使用标准库类来处理面向流的输入和输出:
- iostream处理控制台IO。
- fstream处理命名文件IO。
- stringstream完成内存string的IO。
头文件 | 类型 |
---|---|
iostream | istream,wistream 从流读取数据。ostream,wostream 向流写入数据。iostream,wiostream 读写流。 |
fstream | ifstream,wifstream 从文件读取数据。 ofstream,wofstream 向文件写入数据。fstream,wfstream 读写文件。 |
sstream | istringstream,wistringstream 从string读取数据。ostringstream,wostringstream 向string写入数据。stringstream,wstringstream 读写string。 |
*名字以w开头,代表宽字符,例如,wistream读宽字符。
*使用iostream,fstream,stringstream,可以不用考虑是否是宽字符类型。
二、输出缓冲
每个输出流都管理一个缓冲区,用来保存程序读写的数据。
cout<<"Hello,World!";
文本串可能立即打印出来,也有可能被操作系统保存缓存区中,随后再打印出来。
想要立即打印出来(数据真正写到输出设备或文件)就需要刷新缓冲区。
导致缓冲刷新的原因:
- 程序正常结束,作为main函数的return操作的一部分,刷新缓冲区。
- 缓冲区满时,刷新缓冲区。
- 我们可以使用操作符如endl ,来显示的刷新缓冲区。
操作符flush ,刷新缓冲区,但不输出任何额外的字符;ends 向缓冲区插入一个空字符,然后刷新缓冲区。
例:
cpp
cout<<"Hi!"<<endl; //输出Hi!和一个换行,然后刷新缓冲区
cout<<"hi!"<<flush; //输出hi,然后刷新缓冲区
cout<<"hi!"<<ends; //输出hi和一个空字符,然后刷新缓冲区
- 每个输出操作后,可以使用操作符unitbuf 设置流的内部状态,来清空缓冲区。默认情况下,对cerr是设置unitbuf的,因此写到cerr的内容,都是立即刷新的。
如果想每次输出操作后都刷新缓冲区,可以使用unitbuf 操纵符。它告诉流接下来的每次写操作之后都进行一次flush 操作。
nounitbuf操作符重置流,使其恢复使用正常的系统管理的缓冲区刷新机制。
cpp
cout<<unitbuf; //之后所有输出操作都会立即刷新缓冲区。
.........
cout<<nounitbuf; //回到正常的缓冲方式。
如果程序崩溃,输出缓冲区不会被刷新。
- 一个输出流可能被关联到另一个流。默认情况下,cin和cerr都关联到cout,当读cin或写cerr都会导致cout的缓冲区被刷新。
tie有2个重载的版本。一个版本不带参数,返回指向输出流的指针。第二个版本接受一个指向ostream的指针,将自己关联到此ostream,即x.tie(&o)将流x关联到输出流o。
cpp
cin.tie(&cout); //cin关联到cout
cin.tie(&cerr); //cin关联到cerr,读取cin时会刷新cerr
每个流同时最多关联到一个流,但多个流可以关联到同一个ostream。
三、文件输入输出
1、可以用IO运算符(<< , >> )来读写文件,可以用getline 从一个ifstream读取数据。
2、 fstream特有的操作。
fstream fstrm | 创建一个未绑定的文件流。 |
---|---|
fstream fstrm(s) | 创建一个fstream,并打开一个名字为s的文件。s可以是string类型或者指向c风格字符串的指针 |
fsream fstrm(s,mode) | 与前一个构造函数类似,但按照指定mode打开文件 |
fstrm.open(s) | 打开名为s的文件,并将文件与fstrm绑定。等价于fstream fstrm(s) |
fstrm.close() | 关闭与fstrm绑定的文件。返回void。 |
fstrm.is_open() | 返回一个bool值,指出与fstrm关联的文件是否成功打开且尚未关闭。 |
cpp
ifstream in(ifile); //打开指定文件。
ofstream out; //未绑定文件
out.open(ifile) //打开指定文件
if(out) //检查open是否成功,open成功,我们可以使用文件了,
.......
in.close();
out.close();
3、如果目前文件流已经关联了一个文件,为了将文件流关联到另外一个文件,必须首先关闭已经关联的文件。一旦文件成功关闭,我们可以打开新的文件。
当一个fstream对象离开作用域时,与之关联的文件会自动关闭。当一个fstream对象被销毁时,close会自动被调用。
4、文件模式(file mode)
cpp
fstream fstrm(s,mode);
in | 以读方式打开 |
---|---|
out | 以写方式打开 |
app | 每次读写都定位到文件末尾 |
ate | 打开文件后立即定位到文件末尾 |
binary | 以二进制方式进行IO |
trunc | 截断文件 |
5、每个文件流类型都定义了一个默认的文件模式,当我们未指定文件模式时,就使用默认的模式。与ifsream关联的文件默认以in模式打开;ofstream关联的默认以out模式打开。与fstream关联的文件默认以in和out模式打开。
6,、默认情况下,当我们打开一个ofstream时,文件的内容会被丢弃。阻止一个ofstream清空给定文件内容的方法是同时指定app模式。
cpp
ofstream app("file",ofstream::app); //隐含为输出模式
ofstream app("file",ofstream::out|ofstream::app); //两者等价
四、string流
1、sstream头文件提供了对string的操作。
istringstream | 从string读取数据 |
---|---|
ostringstream | 向string写入数据 |
stringstream | 既可从string读数据也可向string写数据 |
2、stringstream特有的操作
sstream strm | strm是一个未绑定的stringstream对象 |
---|---|
sstream strm(s) | strm是一个sstream对象,保存string s 的一个拷贝。 |
strm.str() | 返回strm所保存的string的拷贝 |
strm.str(s) | 将string s拷贝到strm中,返回void |
3、当我们的某些工作是对整行文本进行处理,而其他工作是处理行内的单个单词时,通常可以使用istringstream。
例:
我们的输入文件看起来可能是这样的:
morgan 2015552368 8625550123
drew 9735550130
lee 6095550132 2015550175 800555000
代码:
cpp
//成员默认为公有
struct PersonInfo{
string name;
vector<string> phones;
};
string line,word; //分别保存来自输入的一行和单词
vector<PersonInfo> people; //保存来自输入的所有记录
while(getline(cin,line)){
PersonInfo info;
istringstream record(line); //将记录绑定到刚读入的行
record>>info.name; //读取名字
while(record>>word){ //读取电话号码
info.phones.push_back(word);
}
people.push_back(info);
}
4、当我们逐步构造输出,希望最后一起打印时,ostringstream是很有用的。
我们使用标准的输出运算符(<<)向这些对象(类型为 ostringstream)写入数据,但这些"写入"操作实际转化为string操作,分别向这些对象中的string对象添加字符。
五、输入输出格式
总述
标准库定义了一组操纵符来修改流的格式状态。大多数改变格式状态的操纵符都是设置/复原成对的。当操纵符改变流的格式状态时,通常改变后的状态对所有后续IO都生效。
boolalpha | 将true和false输出为字符串 |
*noboolalpha | 将true和false输出为1,0 |
showbase | 生成显示进制的前缀 |
*noshowbase | 不生成表示进制的前缀 |
showpoint | 对浮点数总显示小数点 |
*noshowbase | 只有当浮点值包含小数部分时才显示小数点 |
showpos | 对非负数显示+ |
*noshowpos | 对非负数不显示+ |
uppercase | 十六进制打印0X,科学计数法打印E |
*nouppercase | 十六进制打印0x,科学计数法打印e |
*dec | 整型值显示为十进制 |
hex | 整型值显示为十六进制 |
oct | 整型值显示为八进制 |
left | 在值的右侧填充字符 |
right | 在值的左侧填充字符 |
internal在符号和值之间填充字符 | |
fixed | 浮点值显示为定点十进制 |
scientific | 浮点值显示科学计数法 |
hexfloat | 浮点值显示十六进制 |
defaultfloat | 重置浮点数格式为十进制 |
umitbuf | 每次输出操作后都刷新缓冲区 |
*nounitbuf | 恢复正常刷新缓冲区方式 |
*skipws | 输入运算符跳过空白符 |
noskipws | 输入运算符不跳过空白符 |
flush | 刷新打印缓冲出 |
ends | 插入空字符,然后刷新打印缓冲区 |
endl | 插入换行,然后刷新打印缓冲区 |
*表示默认流状态 |
1、控制布尔值的格式
boolalpha 操纵符
noboolalpha*复原
cpp
cout<<true<<" "<<false<<endl; //输出1 0
cout<<boolalpha<<true<<" "<<false<<endl; // 输出true false
cout<<true<<" "<<false<<endl; // 因为改变了输出格式,还没有复原,所有输出 true false
cout<<noboolalpha; //复原
cout<<true<<" "<<false<<endl; //因为恢复了默认格式,所以输出 1,0
2、指定整型值的进制
默认情况下是十进制。
操纵符如下:
十六进制:hex
八进制:oct
十进制:dec
这些操纵符只影响整型运算对象,浮点值的表示形式不受影响。
cpp
cout<<20<<" "<<1024<<endl; //输出 20 1024
cout<<hex<<20<<" "<<1024<<endl; //14 400
cout<<oct<<20<<" "<<1024<<endl; //24 2000
cout<<dec<<20<<" "<<1024<<endl; //20 1024
//指定进制想要变回默认10进制,就指定它类型为10进制就可以
3、在输出中指出进制
使用showbase 操纵符
noshowbase恢复默认状态。
cpp
cout<<showbase; //打印整型时显示进制
cout<<20<<" "<<1024<<endl; //输出 20 1024
cout<<hex<<20<<" "<<1024<<endl; //0x14 0x400
cout<<oct<<20<<" "<<1024<<endl; //024 02000
cout<<dec<<20<<" "<<1024<<endl; //20 1024
cout<<noshowbase; // 恢复流状态,不再显示整型值的进制
//默认情况下十六进制是小写打印,使用uppercase操纵符,可以变成大写。nouppercase ,变回小写
cout<<uppercase<<showbase<<hex<<20<<" "<<1024<<endl; //0X14 0X400
4、控制浮点数格式
可以控制浮点数输出三种格式
- 以多高精度打印浮点数。
- 数值是打印为十六进制、定点十进制、还是科学计数法形式。
- 对于没有小数部分的浮点值是否打印小数点。
默认情况下,
- 浮点数按6位数字精度打印。
- 如果浮点值没有小数部分,则不打印小数点。
- 标准库会选择一种可读性更好的格式,来输出。
4.1、 指定打印精度
我们可以通过调用IO对象的precision成员或使用setprecision操纵符来改变精度。
setprecision操纵符定义在头文件iomanip中。
cpp
//第一种方法
cout.precision(12);
double pi=3.14156;
cout<<pi<<endl;
//第二种方法
cout<<setprecision(12);
cout<<pi<<endl;
//默认情况下精度为6,想恢复,把它设置成6即可
5、输出空白
- setw指定下一个数字或字符串值的最小空间
- left 表示左对齐
- right 表示右对齐
- internal 控制负数的符号的位置。用空格填满所有中间空间
- setfill 允许指定一个字符代替默认的空格来补白输出
cpp
#include<iostream>
#include<iomanip>
using namespace std;
int main() {
int i = -16;
double d = 3.14159;
cout << setw(12) << i <<" next" << endl;
cout << setw(12) << d <<" next" << endl;
cout << left<< setw(12) << i << " next" << endl
<< setw(12) << d << " next" << endl;
cout << right << setw(12) << i << " next" << endl
<< setw(12) << d << " next" << endl;
cout << internal << setw(12) << i << " next" << endl
<< setw(12) << d << " next" << endl;
cout << setfill('#') << setw(12) << i << " next" << endl
<< setw(12) << d << " next" << endl<<setfill(' ');
return 0;
}
六、未格式化的输入输出操作
标准库还提供了一组底层操作,支持未格式化IO,这些操作允许我们将一个流当做一个无解释的字节序列来处理
is.get(ch) | 读取一个字节存入字符ch,返回is |
os.put(ch) | 将字符输出到os,返回os |
is.get() | 将is的下一个字节作为int返回 |
is.putback() | 将字符ch放回is,返回is |
is.unget() | 将is向后移动一个字符,返回is |
is.peek() | 将下一个字节作为int返回,但不从流中删除它 |
cpp
#include<iostream>
#include<iomanip>
using namespace std;
int main() {
int ch;
while (ch=cin.get())
cout.put(ch);
return 0;
}
cpp
#include<iostream>
#include<iomanip>
using namespace std;
int main() {
char ch;
while (cin.get(ch))
cout.put(ch);
return 0;
}
运行结果:
输入什么,就输出什么。
tellg() | 返回输入流中的标记的当前位置 |
tellp() | 返回输出流中的标记的当前位置 |
seekg(pos) | 重定位到给定地址 |
seekp(pos) | 重定位到给定地址 |
seekp(off,from) | 将标记定位到from之前或之后off个字符 |
seekg(off,from) | 将标记定位到from之前或之后off个字符 |
这些操作主要用在ofstram,ifstream,ostringstream,istringstream.
pos通常是tellg或者tellp返回的值。