(十)StringStream Class
1、不要用char*版本。
2、操作和fstream一模一样
成员函数 |
意义 |
str() |
将缓冲区内容作为string返回 |
str(string) |
将string设置为缓冲区内容 |
str(“”) |
删除缓冲区 |
tellg() |
当前读取位置 |
seekg(pos) |
设置绝对读取位置 |
seekg(offset, pos) |
设置相对pos的偏移读取位置 |
|
|
tellp() |
当前写入位置 |
seekp(pos) |
绝对写入位置 |
seekp(offset, pos) |
设置相对pos的偏移写入位置 |
|
|
常数 |
意义 |
beg |
开头 |
cur |
当前位置 |
end |
结尾 |
|
|
标志 |
意义 |
in |
读取,istringstream的默认模式 |
out |
写入,ostringstream的默认模式 |
app |
写入时添加到尾部 |
ate |
读写时,读写位置移动到文件末尾 |
trunc |
将先前的文件内容移除 |
binary |
不替换特殊字符,这样可以保留其他系统文件格式。如果是二进制文件也应该用他 |
(十一)自定义IO操作符
1、output经典做法,以输出分数为例子:
inline std::ostream&operator << (std::ostream& strm, const Fraction& f)
{
strm << f.num() << “ / ” <<f.den();
return strm;
}
就一般情况而言,上面的已经足够了。但是如果要格式化输出,就会出问题。下面给出格式化输出的方法,核心思想是变化一个string,然后用iostream的方法处理string:
template <class charT, class traits>
inline
std::basic_ostream<charT , traits>&
operator << (std::basic_ostream<charT,traits>& strm, const Fraction& f)
{
std::basic_ostringstream<charT, traits> s;
s.copyfmt(strm);//复制strm的格式化标志
s.width(0);
s << f.num() << ‘/’ <<f.den();//变成string
strm << s.str();
return strm;
}
缺点就是慢。
2、input经典做法:
inline std::istream& operator >>(std::istream& strm, Fraction& f)
{
int n,d;
strm >> n;
strm.ignore();
strm >> d;
return strm;
}
就一般情况而言,上面的已经足够了。问题一样,格式化不行。同样给出全面的解法:
template <class charT, class traits>
inline
std::basic_istream<charT , traits>&
operator >> (std::basic_istream<charT,traits>& strm, Fraction& f)
{
int n,d;
strm >> n;
if(strm.peek() == ‘/’)//检查下一个字符
{
strm.ignore();
strm >> d;
}
else
{
d = 1;
}
if (d == 0)//分母不能是0,不然抛出异常
{
strm.setstate(std::ios::failbit);
return strm;
}
if(strm)
{
f = Fraction(n,d);
}
return strm;
}
缺点同样是很慢。
3、如果需要在类中使用私有成员,可以建立专门的读取函数:
class Fraction
{
public:
virtual printOn(std::ostream& strm);
virtual scanFrom(std::istream& strm);
}
inline std::istream& operator >>(std::istream& strm, Fraction& f)
{
f.scanFrom(strm);
return strm;
}
具体操作在类成员函数中进行。
4、不使用<< 和 >> ,直接进行自定义重载,利用stream的基本函数。上面的重载函数归根到底还是使用了<<和>>,这样函数自己做完了所有的工作。但是现在不用<< 和>>,这样很多工作要自己做。偷懒的方法就是使用sentry完成预备工作。
书上是用class Row来演示的,row代表文本处理的一行。重载输出函数如下:
std::ostream& operator << (std::ostream& strm,const Row& row)
{
std::ostream::sentry se(strm)
if(se)
{
strm.write(row.c_str(), row.len());
}
return strm;
}
5、自定义格式标志符
这里指的自定义格式符号,是独立于std::ios_base的格式符号之外的。所以我们要获取一个用户自定义空间,然后管理这个玩意儿。
static const int iword_index = std::ios_base::xalloc();//获取用户自定义位置
std::ostream& fraction_space(std::ostream& strm)
{
strm.iword(iword_index) = true;//设置标志位
return strm;
}
std::ostream& operator << (std::ostream& strm,const Fraction &f)
{
if (strm.iword(iword_index))//检查标志位
{
........
}
else
{
........
}
return strm;
}
copyfmt函数会复制包括iword在内的标志位。和iword一样的函数还有pword,不常用。
(十一)stream buffer
1、streambuffer是真正执行读写操作的类。你可以直接使用它来进行读写操作,速度快的与C函数有的一拼,但是烦。
2、buffer的操作和内部细节不要深究,可以学习stream bufferiterator。
3、输出缓冲区迭代器ostreambuf_iterator用法:
std::ostreambuf_iterator<char>bufWriter(std::cout);
std::stringhello(“hello world.\n”);
std::copy(hello.begin(),hello.end(), bufWriter);
4、输入缓冲区迭代器。其实仔细观察可以发现他们和istream_iterator的操作很像。
istreambuf_iterator<char>inpos(cin);
istreambuf_iterator<char>endpos;//结束标志符
while(inpos !=endpos)
{
++inpos;
}
5、
输出迭代器 |
|
代码 |
作用 |
ostreambuf_iterator<char> (ostream) |
针对ostream的迭代器 |
ostreambuf_iterator<char>(buffer_ptr) |
成为buffer_ptr缓冲区的迭代器 |
*iter |
返回iter |
iter = c |
调用sputc(c),缓冲区写入字符c |
++iter |
返回iter |
iter++ |
返回iter |
failed() |
判断迭代器是否可以改写 |
|
|
输入迭代器 |
|
istreambuf_iterator<char>() |
结束标识符 |
istreambuf_iterator<char>(istream) |
istream迭代器 |
istreambuf_itrator<char>(buffer_ptr) |
成为buffer_ptr缓冲区的迭代器 |
*iter |
返回iter |
++iter |
调用sbumpc()函数,读取下一个字符 |
iter++ |
调用sbumpc()函数,读取下一个字符 |
iter1.equal(iter2) |
判断迭代器相等 |
iter1 == iter2 |
判断迭代器相等 |
iter1 != iter2 |
判断迭代器不相等 |
6、streambuffer的自定义不要管啦,《stl源码剖析》也没有讲,不过估计也用不到,人家google代码规范都说不要用stream了。
7、直接使用stream缓冲区,很快,之前已经使用过了,典型例子:
std::cout<< std::cin.rdbuf();