IO 库---笔记

格式状态

除了条件状态之外,每个iostream对象还维持一个控制IO格式 化细节的格式状态
–格式化状态控制格式化特征。关键是:标准库还定义了一组操作符 来修改对象的状态
好了,废话不多说,直接上表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
====================iostream中定义的操作符=================
boolalpha 将真和假显示为字符串
noboolalpha 将真和假显示为1、0
showbase 产生指出数的基数的前缀
noshowbase 不产生
showpoint 总是显示小数点
noshowpoint 不显示
showpos 显示非负数的+
noshowpos 不显示
uppercase 在十六进制中打印0X,科学技术法E
nouppercase 小写
dec 十进制
hex 十六进制
oct 八进制
left 在值的右边增加填充字符(左对齐)
right 左边~(右对齐)
internal 在符号和值之间增加填充字符(居中)
fixed 用小数形式显示浮点数
scientific 用科学计数法显示浮点数
flush 刷新ostream缓冲区
ends 插入空字符,刷新ostream
endl
unitbuf 在每个输出操作符之后刷新缓冲区
nounitbuf 恢复常规缓冲区刷新
skipws 为输入操作符跳过空白
noskipws 不输入
ws “吃掉”空白
=========================================================
========================iomanip中定义的操作符============
setfill(ch) 用ch填充空白
setprecision(n) 将浮点数的精度设为n
setw(w) 读写w个字符的值
setbase 按基数b输出整数
=========================================================
//注意:改变流格式状态的操作符为后续的IO保留改变<br>
//也就是说,你一旦用操作符设置了相关格式,以后也不会改变了<br>
//其实,这也是为什么大多数格式提供设置和复原对 的原因<br>
--取消操纵符的任何状态改变通常是最好的。一般而言,流应该在
每个IO操作之后处于通常的默认状态<br>
--<font color='red'>用flags操纵恢复格式状态</font><br>
//这个方法真的不错哦,好好想想

flags操作类似于管理流条件状态的rdstate和setstate操作,这种情况下 标准库定义了一对flags函数:

1> 不带实参的flags() 返回流的当前格式状态。返回值为fmtflags的 标准库的定义类型

2> flags(arg) 接受一个实参并将流格式置为实参所指定的格式

使用范例:

1
2
3
4
5
6
void display(ostream &os)
{
ostream::fmtflags curr = os.flags();
//在此之间定义你想要做的事,即使是改变流的输出格式而忘记恢复也可以
os.flags(curr);
}

—控制输出格式
—-控制数值的表示
—-控制填充符的数量和布局
## 1) 布尔值 ##

1
2
3
4
5
6
cout << "default bool values: "
<< true << " " << false
<< "\nalpha bool values: "
<< boolalpha
<< true << " " << false
<< endl;

结果是:

1
2
default bool values: 1 0
alpha bool values: true false

要取消 cout 的格式状态改变,必须应用 noboolalpha:

1
2
3
4
5
bool bool_val;
cout << boolalpha
<< bool_val
<< noboolalpha;
//这样就恢复原来的状态,可是如果不恢复呢?会出现什么问题嘞/

(2) 指定整型值的基数

通过使用hex,oct,dec,可以上数字以不同的进制显示 实例:

1
2
3
4
5
6
const int k = 1024;
cout<<k<<endl
<<oct<<k<<endl
<<hex<<k<<endl
<<dec<<k<<endl;
//大家可以亲测下,顺便手上熟悉熟悉

(3) 指出输出的基数

showbase可以让输出显示他们所使用的基数(该数字是X进制的)

-以0x为前导表示十六进制
-以0为前导表示八进制
-十进制无显示

使用方法://当然得结合dec , hex , oct啦

1
2
3
4
5
6
7
8
9
10
11
const int ival = 15 , jval = 1024;
cout<<showbase;
cout << "default: ival = " << ival
<< " jval = " << jval << endl;
cout << "printed in octal: ival = " << oct << ival
<< " jval = " << jval << endl;
cout << "printed in hexadecimal: ival = " << hex << ival
<< " jval = " << jval << endl;
cout << "printed in decimal: ival = " << dec << ival
<< " jval = " << jval << endl;
cout << noshowbase; // reset state of the stream

修改后的输出使得基础值到底是什么很清楚:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
default: ival = 15 jval = 1024
printed in octal: ival = 017 jval = 02000
printed in hexadecimal: ival = 0xf jval = 0x400
printed in decimal: ival = 15 jval = 1024
```
顺便说一句:使用noshowbase操纵符重置cout
还有,默认情况下,0x的‘x’都是小写,如果你喜欢大写,就用
uppercase<br>
使用示例:
```c++
cout << uppercase << showbase << hex
1023
<< "printed in hexadecimal: ival = " << ival
<< " jval = " << jval << endl
<< nouppercase << endl;

前面的程序产生下面的输出:

1
2
printed in hexadecimal: ival = 0XF jval = 0X400
//亲~~~~~记得恢复哦

(4) 控制浮点值的格式

对浮点值的格式化,可以控制下面的三个方面

-精度:显示多少位数字
-计数法:用小数还是科学技术
-对是整数的浮点值的小数点的处理
//默认情况下,精度为六位有效数字

通过名为precision的成员函数,或者通过使用setprecision precision

-接受一个int值并将精度设置为那个新值,返回先前的精度值
-不接受任何形参,返回当前精度值
setprecision,设置精度

示例:

1
2
3
4
5
6
7
8
9
10
11
12
cout<<"当前的精度"<<cout.precisions
<<" value"<<squrt(2)<<endl;
cout.precision(12);
cout<<"当前的精度"<<cout.precisions
<<" value"<<squrt(2)<<endl;
cout<<setprecision(3);
cout<<"当前的精度"<<cout.precisions
<<" value"<<squrt(2)<<endl;
编译并执行后,程序产生下面的输出:
Precision: 6, Value: 1.41421
Precision: 12, Value: 1.41421356237
Precision: 3, Value: 1.41

(5) 控制计数法

如果希望强制科学计数法或固定小数位数表示

-scientific操纵符将流变为使用科学计数法
-fixed操纵符将流为使用固定位数小树表示

恢复浮点值的默认计数法

通过调用unsetf成员来取消这两个操作所做的改变。

要将流恢复为默认,将名为floatfield的标准库定义的值传给unsetf函数

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cout<<unsetf(ostream::floatfield);
cout << sqrt(2.0) << '\n' << endl;
cout << "scientific: " << scientific << sqrt(2.0) << '\n'
<< "fixed decimal: " << fixed << sqrt(2.0) << "\n\n";
cout << uppercase
<< "scientific: " << scientific << sqrt(2.0) << '\n'
<< "fixed decimal: " << fixed << sqrt(2.0) << endl
<< nouppercase;
// reset to default handling for notation
cout.unsetf(ostream::floatfield);
cout << '\n' << sqrt(2.0) << endl;
产生如下输出:
1.41421
scientific: 1.414214e+00
fixed decimal: 1.414214
scientific: 1.414214E+00
fixed decimal: 1.414214
1.41421

(6)显示小数点

就是showpoint啦,直接桑例子!!!

1
2
3
cout<<10.0<<endl; // print 10
cout<<showpoint<<10.0
<<noshowpoint<<endl; //print 10.0000

(7)填充输出

1
2
3
4
5
6
setw 指定下一个数值或字符串的最小间隔
left 左对齐输出
right 右对齐输出
internal 符号靠左,数值靠右 输出
setfill 使我们能够指定填充输出时使用的另一个字符。默认情况下,
值是空格。

下面程序段说明了这些操纵符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int i = -16;
double d = 3.14159;
// pad first column to use minimum of 12 positions in the output
cout << "i: " << setw(12) << i << "next col" << '\n'
<< "d: " << setw(12) << d << "next col" << '\n';
// pad first column and left-justify all columns
cout << left
<< "i: " << setw(12) << i << "next col" << '\n'
<< "d: " << setw(12) << d << "next col" << '\n'
<< right; // restore normal justification
// pad first column and right-justify all columns
cout << right
<< "i: " << setw(12) << i << "next col" << '\n'
<< "d: " << setw(12) << d << "next col" << '\n';
// pad first column but put the padding internal to the field
cout << internal
<< "i: " << setw(12) << i << "next col" << '\n'
<< "d: " << setw(12) << d << "next col" << '\n';
// pad first column, using # as the pad character
cout << setfill('#')
<< "i: " << setw(12) << i << "next col" << '\n'
<< "d: " << setw(12) << d << "next col" << '\n'
<< setfill(' '); // restore normal pad character
执行时,该程序段产生如下输出:
i: -16next col
d: 3.14159next col
1027
i: -16 next col
d: 3.14159 next col
i: -16next col
d: 3.14159next col
i: - 16next col
d: 3.14159next col
i: -#########16next col
d: #####3.14159next col

以上就是个操作符的详细介绍了

控制输入格式化

--默认情况下,输入操作符忽略空白(空格,制表符,换行符,进纸 回车)
如下:
1
2
3
4
5
6
7
while(cin>>ch)
cout<<ch;
给定输入序列
a b c
d
结果:
abcd

如果使用noskipws操作符导致输入操作符读而不跳过空白

1
2
3
4
5
6
7
8
9
10
11
cin>>noskipws;
while(cin>>ch)
cout<<ch;
cin>>skipws;
输入:
a b c
d
输出:
a b c
d

—-单字节操作

特点:一个字节一个字节的读

1
2
3
4
5
6
char ch;
while(cin.get(ch))
cout.put(ch);
输出:
a b c
d

单字节低级IO操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
=============单字节低级IO操作========================
is.get(ch)
os.put(ch)
is.get() 返回is的下一个字节作为一个int//注意是int值,而不是char
is.putback(ch) 将字符ch放回is,返回is
is.unget() 将is退回一个字节,返回is
is.peek() 将下一个字节作为int值返回但不移动它
=======================================================
--在流上倒退
有时我们需要读一个字符才知道还没有为他做好准备,在这种情况下
,希望将字符放回流中,有三中方式;
---peek 返回输入流上下一个字符的副本但不改变流。peek的
返回值留在流上,且将是下一个被检索(就是下一次读的还是他!)
---unget 使输入流倒退,以便最后返回的值仍在流上。
---putback unget 的复杂版本,一般不用的~~~~不管他(其实 我也不是很明白……)
//一般而言,保证能够在下一次读之前放回最多一个值,也就是说,不保证能
//够连续调用 putback 或 unget 而没有介于其间的读操作。

下面的操作就比较常用啦

1
2
3
4
5
6
7
8
9
10
=======================多字节低级IO操作================
is.get(sink , size , delim) 或遇到文件结束符才结束。如果出现了delim
,就把他留在流中,不读入到sink
is.getline(sink , size , delim) 与三个实参的get形参类似,但读并丢弃delim
is.read(sink , size) 对size个字节到数组sink,返回is
is.gcount() 计数,不过这个计数有点怪,看后面的例子就知道啦
os.write(source , size) 将size个字从数组source写至os
is.ignore(size , delim) 读并忽略至多size个字符,直到遇见delim,但不包括
默认情况下,size是1,而delim为文件结束符
=========================================================================

警告:低级例程容易出错

例如返回int值的IO操作

将get或其他返回int值的函数的返回值赋值给char对象而不是int对象,这是很常见的错误 例如,大家经常会这么写:

1
2
3
4
5
6
7
char ch
while((ch = cin.get()) != EOF)
cout.put(ch);
但事实上这是个死循环,可是编译器不检测这样的错误!!!
//事实上:为了不要求我们知道返回的实际值,头文件iostream定义了
//名为EOF的const,可以用它来测试get的返回值是否为文件结束符

允许给定字符集使用 char 范围的每一个值来表示实际字符,因此,该范围中没有额外值用来表示文件结束符。 !!!
相反,这些函数将字符转化为unsigned char,然后提升为 int 。 这就是问题所在啦:
当get返回EOF的时候,那个值将被转换为unsigned char值,转换后的值不再等于EOF 的整型值,循环将永远执行!!!

所以正确的方式为:

1
2
3
int ch;
while((ch = cin.get()) != EOF)
cout.put(ch);

确定读入多少字符:

这里就是gcount的使用方法了,我们通过一个例子来了解它的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
#include<iostream>
using namespace std;
int main()
{
char *s;
s = new char[1024];
cin.getline(s, 1024);
//cin.read(s, 1024);
cout<<cin.gcount()<<endl;
return 0;
}
由此可见:cin.gcount用于显示上一次cin.getline或
cin.read(char *buf, int n)的输入的字符数,含末尾
'\0',但不能用于cin>>str中的str长度的测算。
cin.read的结束符只能用Ctrl + z

流的随机访问

seek 和 tell 函数

为了支持随机访问,IO类型维持一个标记。该标记决定下一个读或写 将要发生的位置
seek 指定位置重新安置该标记
tell 返回当前位置

1
2
3
4
5
6
===========seek 和 tell ============
seekg 重新定位输入流中的标记
tellg 返回输入流中标记的当前位置
seekp 重新定位输出流中的标记
tellp 返回输出流中标记的当前位置
=======================================

当然,g 代表的是 get 用于输入流
p 代表的是put ,用于输出流
切记,不可混了
但是iostream 对象、fstream 对象或 stringstream 对象对相 关流既能读又能写,可以在这些类型的对象上使用 g 或 p 版本的任何一个

–只有一个标记 虽然seek和tell在输入和输出中区分使用,但是标记只有一个哦~

–随机访问只针对 fstream 和 sstream

重新定位标记

seekg 函数和 seekp 函数用于改变文件或 string 对象中的读写位置。调 用 seekg 函数之后,改变流中的读位置,seekp 函数调用将位置置于下一个写 将发生的地方

seek函数有两个版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
--移动到文件的一个“绝对”的位置
--移动到给定位置的字节偏移量处
seekg(new_posion) ; //set read mark
seekp(new_posion); //set write mark
seekg(offset , dir);
seekp(ofset , dir);
===============seek实参的偏移量============
beg 流的开头
cur 流的当前位置
end 流的末尾
============================================

这些函数的实参类型和返回类型是与机器相关的类型,在istream和 ostream中定义。名为 pos_typeoff_type 的类型分别表示文件位置和 从该位置的偏移量 (ofset position)

访问标记

当前位置由 tell返回,一般用于记住一个位置,以便seek使用
程序实例:

1
2
3
4
ostringstream writeStr;
ostringstream::pos_type mark = writeStr.tellp();
if(condition)
writeStr.seekp(mark);

好啦,该介绍的都介绍了,运用一下吧:

题:

1
2
3
4
5
6
7
8
9
10
11
12
假定给定一个文件来读,我们将在文件的末尾写一个新行,该行包含每一行开头的相对位置(即偏移量)。
例如一个文件:
abcd
efg
hi
j
经过程序修改之后的文件:
abcd
efg
hi
j
5 9 12 14
自己动手实现哦,代码见下篇

热评文章