文章目录
IO库
8.1 IO类
三大IO类
带w
前缀的为对应的宽字符版本
cin
、cout
、cerr
、clog
都有对应的宽字符版本wcin
、wcout
、wcerr
、wclog
头文件和类的关系:
-
iostream头文件
- istream,wistream
- ostream,wostream
- iostream,wiostream
-
fstream头文件
- ifstream,wifstream
- ofstream,wofstream
- fstream,wfstream
-
sstream头文件
- istringstream,wistringstream
- ostringstream,wostringstream
- stringstream,wstringstream
IO类之间的继承关系:
8.1.1 IO对象拷贝或赋值
注意:
-
流对象不能进行拷贝和赋值
-
读写IO对象会改变流的状态,因此传递和返回的流的引用不能是const的
8.1.2 条件状态
strm
指的是任意一个IO类,置位
指将标志数对应位设置为1,复位
指将标志数对应位设置为0
条件状态 | 描述 | 值 |
---|---|---|
strm::iostate | iostate是一种机器相关的类型,提供了条件状态的完整功能(VS2019中为int型) | |
strm::badbit | 用来指出流已崩溃 | 4 |
strm::failbit | 用来指出一个IO操作失败了 | 2 |
strm::eofbit | 用来指出流到达了文件末尾 | 1 |
strm::goodbit | 用来指出流未处于错误状态。此值保证为零 | 0 |
s.eof() | 若流s的eofbit置位,则返回true | |
s.fail() | 若流s的failbit或badbit置位,则返回true | |
s.bad() | 如流s的badbit置位,则返回true | |
s.good() | 如流s处于有效状态,则返回true | |
s.clear() | 将流s中所有状态位复位,将流的状态位设置为有效。返回void | |
s.clear(flags) | 根据给定的flags标志位,将流s中对应状态位复位。flags类型为strm::iostate。返回viod | |
s.setstate(flags) | 根据给定的flags标志位,将流s中对应状态位置位。flags类型为strm::iostate。返回void | |
s.rdstate() | 返回流的当前状态,返回值类型为strm::iostate |
注意:一旦一个流发生错误,其上后续的IO操作都会失败。特别要注意的是读取文件的时候经常会读取到文件末尾,eof
和fail
被置位,导致流失效
源文件中有一个**strm::iostate***(实质为int***)类型的_Mystate变量
_Mystate各个位的含义:
状态位 | badbit | failbit | eofbit | ||||||
---|---|---|---|---|---|---|---|---|---|
位下标 | … | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
函数源代码:
(1)
iostate rdstate() const {
return _Mystate;
}
(2)
bool good() const {
return rdstate() == ios_base::goodbit;
}
bool eof() const {
return rdstate() & ios_base::eofbit;
}
//剩下的几个函数以此类推...
(3)
//由下面的函数可得,想要清除哪个位就将传入值中的那个位置0
void clear(iostate _State = goodbit) {
clear(_State);
}
void clear(iostate _State) {
_State &= _Statmask;//_Statmask = 00010111
_Mystate = _State;
}
(4)
void setstate(iostate _State) {
clear(rdstate() | _State);
}
相信你已经明白了为什么
badbit值为4[00000100~b~],failbit的值为2[00000010b],efo的值为1[00000001~b~],goobit的值为0[00000000b]
8.1.3 管理输出缓冲
每个输入输出流对象都有自己的缓冲区
关联输入输出流
当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流。
标准库将cin-cout关联,将cerr-cout关联
tie有两个版本,两个版本返回的都是指向改变之前关联的对象的指针
- 第一个版本:不接受参数
- 第二个版本:接受一个指向ostream的指针,将自己关联到此ostream
我们可以将一个istream对象关联到另一个ostream,也可以将一个ostream关联到另一个ostream
注意:每个流同时最多关联到一个流,但多个流可以同时关联到同一个ostream
8.2 文件输入输出
8.2.1 使用文件流对象
操作 | 描述 |
---|---|
fstream fstrm; | 创建一个未绑定的流。***fstream***是头文件fstream中定义的一个类型 |
fstream fstrm(s); | 创建一个fstream,并打开名为s的文件。s可以是string类型,或者是一个指向C风格字符串的指针。这些构造函数都是explicit的 |
fstream fstrm(s,mode); | 与前一个构造函数相同,但按指定mode打开文件 |
fstrm.open(s); | 打开名为s的文件,并将文件与fstrm绑定。返回void |
fstrm.close(); | 关闭与fstrm绑定的文件。返回void |
fstrm.is_open(); | 返回一个bool值,指出与fstrm关联的文件是否打开成功且尚未关闭 |
注意:
-
同一个流对象再次打开同一个文件会导致打开失败
-
不同对象可以同时打开同一个文件
-
当一个fstream对象被销毁时,close会自动被调用
8.2.2 文件模式
可选打开方式
mode | 描述 |
---|---|
in | 以读方式打开 |
out | 以写方式打开 |
app | 每次写操作前均定位到文件末尾 |
ate | 每次文件打开后立即定位到文件末尾 |
trunc | 截断文件 |
binary | 以二进制方式进行IO |
默认打开方式
对象 | 默认打开方式 |
---|---|
ifstream | in |
ofstream | out |
fstream | in 和 out |
文件打开方式限制:
- 只可以对ofstream或fstream对象设定out模式
- 之可以对ifstream或fstream对象设定in模式
- 只有但out也被设定时才可以设定trunc模式
- 只要trunc模式没被设定,就可以设定app模式。在app模式下,即使没有显示指定out模式,文件也总是以输出方式被打开
- 默认情况下,即使我们没有指定trunc,以out模式打开的文件也会被截断。为了保留out模式打开的文件的内容,我们必须同时指定app模式,这样只会将数据追加写到文件末尾;或者同时指定in模式,即打开文件进行读写操作
- ate和binary模式可用于任何类型的文件流对象,且可以与其他任何文件模式组合使用
8.3 string流
操作 | 描述 |
---|---|
sstream strm | strm是一个为绑定stringstream对象。***sstream***是头文件sstream中定义的一个类型 |
sstream strm(s) | strm是一个 sstream 对象,保存string s的一个拷贝。此构造函数是explicit的 |
strm.str() | 返回strm所保存的string的拷贝 |
strm.str(s) | 将string s拷贝到strm中。返回void |
8.4 IO再探
8.4.1格式化输入输出
操纵符:一个操纵符是一个函数或是一个对象,会影响流的状态,并能作输入或输出运算符的运算对象
**注意:**当操作符改变流的格式状态时,通常改变后的状态对所有后续IO都生效
控制布尔值的格式
使用
std::cout<<boolalpha;
可以将布尔值以单词形式输出,要想调整回来,可以使用
std::cout<<noboolalpha;
指定整型值的进制
符号 | 含义 |
---|---|
dec | 十进制 |
oct | 八进制 |
hex | 十六进制 |
可使用 showbase显示不同进制的前缀,noshowbase取消不同进制的前缀显示
控制浮点数格式
可以调用IO对象的precision成员或使用setprecision操纵符来改变精度
默认情况下 ,浮点数按六位数字精度打印;
如果浮点数没有小数部分,则不会打印小数点;
根据浮点数的值选择打印成顶点十进制或科学计数法形式,标准库会选择可读性好的方式进行显示
操作符前面的*
号表示此模式为默认模式
定义在iostream中的操纵符 | 描述 |
---|---|
boolalpha | 将true和false输出为字符串 |
*noboolalpha | 将true和false输出为1和0 |
showbase | 对整数值输出表示进制的前缀 |
*noshowbase | 不生成表示进制的前缀 |
showpoint | 对浮点数总是显示小数点 |
*noshowpoint | 只有当浮点值包含小数部分时才显示小数点 |
showpos | 对非负数显示+[^对于unsigned类型不会显示+] |
*noshowpos | 对非负数不显示+ |
uppercase | 在十六进制中打印0X,在科学计数法中打印E |
*nouppercase | 在十六进制中打印0x,在科学计数法中打印e |
*dec | 整型值显示为十进制 |
hex | 整型值显示为十六进制 |
oct | 整型值显示为八进制 |
left | 在值的右侧添加填充字符 |
right | 在值的左侧添加填充字符 |
internal | 子符号和值的中间添加填充字符 |
fixed | 浮点值显示为顶点十进制[^固定6位小数] |
scientific | 浮点值显示为科学计数法 |
hexfloat | 浮点值显示为十六进制(C++11新特性) |
*dehexfloat | 重置浮点数格式为十进制(C++11新特性) |
unitbuf | 每次输出操作后都刷新缓冲区 |
*nounitbuf | 恢复正常的缓冲区刷新方式 |
*skipws | 输入运算符跳过空白符 |
noskipws | 输入运算符不跳过空白符[^对string对象无效] |
flush | 刷新ostream缓冲区 |
ends | 插入空字符,然后刷新ostream缓冲区 |
endl | 插入换行,然后刷新ostream缓冲区 |
输出补白
- setw指定下一个数字或字符串值的最小空间(setw只能作用一次)
- left表示左对齐输出
- right表示右对齐输出(默认)
- internal控制负数的符号的位置,它左对齐符号,右对齐值,用空格填满所有中间的空间
- setfill允许制定一个字符代替默认的空格来补白输出
定义在iomanip中的操纵符 | 描述 |
---|---|
setfill(ch) | 用ch填充空白 |
setprecision(s) | 将浮点数精度设置为n |
setw(w) | 读或写值得宽度为w个字符 |
setbase(b) | 将整数输出为b进制 |
8.4.2未格式化的输入\输出操作
需要了解的是,缓冲区中会有一个指针,指向我们当前操作的字符,读取或输出缓冲区内容时,指针会自动++
单字节操作
单字节操作函数 | 描述 |
---|---|
is.get(ch) | 从istream is读取下一个字节存入字符ch中。返回is |
os.put(ch) | 将字符ch输出到ostream os。返回os |
is.get() | 将is的下一个字符作为int返回 |
is.putback(ch) | 将流指针后移一个字节。返回is |
is.unget() | 将流指针后移一个字节。返回is |
is.peek() | 将下一个字节作为int返回,但不移动指针位置 |
函数peek()和无参版本的get版本都以int类型从输入流返回一个字符。
这些函数返回一个int的原因是:可以返回文件尾标志。我们使用char范围中的值来表示一个真实字符,因此,取值范围中没有额外的值可以用来表示文件尾
多字节操作
多字节操作函数 | 描述 |
---|---|
is.get(sink,size,delim) | 从is中读取最多size个字节,并保存在字符数组中,字符数组的起始地址右sink给出。读取过程直至遇到字符delim或读取了size个字节或遇到文件尾时停止。如果遇到了delim,则将其留在输入流中,不读取出来存入sink |
is.getline(sink,size,delim) | 与接受三个参数的get版本类似,但会读取并丢弃delim |
is.read(sink,size) | 读取最多size个字节,存入字符数组sink中。返回is |
is.gcount() | 返回上一个未格式化读取操作从is读取的字节数 |
os.write(source) | 将字符数组source中的最多size个字符写入os。返回os |
is.ignore(size,delim) | 忽略最多size个或者忽略delim前的所有字符(包括delim),哪个先满足就按哪个执行 |
注意:
- is.get()和is.getline()函数会读取size-1个字节,并自动把字符数组最后一个字节赋值为’\0’,而read函数会读取size个字节,不会自动把字符数组最后一个字节赋值为’\0’,需要我们特别注意,否则将有可能导致错误
- 在一台char被实现为unsignend char类型的机器上,下面的循环永远不会停止:*
char ch;
while((ch = cin.get()) != EOF){
cout.put(ch);
}
//原因如下
//EOF = -1 ->源码:1...00000001 ->反码:1...11111110 ->补码:1...11111111
//ch取得EOF低八位,所以 ch = 11111111(二进制) -> ch = 255,而EOF = -1,所以永远不会相等
8.4.3 流随机访问
随机IO本质上是依赖于系统的,为了理解如何使用这些特性,你必须查询系统文档
注意:istream和ostream类型通常不支持随机访问
后缀g
可以理解为get,p
可以理解为put
随机访问函数 | 描述 |
---|---|
tellg() | 返回一个输入流中(tellg)标志的当前位置 |
tellp() | 返回一个输出流中(tellp)标志的当前位置 |
seekg(pos) | 在一个输入流中将标志重定位到给定的绝对地址。pos通常是前一个tellg返回的值 |
seekp(pos) | 同上,只不过运用的是输出流 |
seekg(off,from) | 在一个输入流将标志定位到from之前或之后off个字符(off表示后移,off为负表示前移) |
seekp(off,from) | 同上,… |
from的值 | 描述 |
---|---|
beg | 流开始位置 |
cur | 流当前位置 |
end | 流结尾位置 |
注意:标准库区分seek和tell函数的g
和p
版本这一特性可能会导致误解。即使标准库进行了区分,但它在一个流中之维护单一的标志——并不存在独立的读标志和写标志