流和缓冲区
- C++程序把输入和输出看作字节流。输入时,程序从输入流中抽取字节;输出时,程序将字节插入到输出流中。
- 流充当了程序和流源或流目标之间的桥梁。C++程序只是检查字节流,而不需要知道字节来自何方和去向何处。
- 使用缓冲区可以更高效地处理输入和输出。
- 处理输入时,缓冲区通常从磁盘读取大量信息,然后每次从缓冲区里读取一个字节。
- 处理输出时,程序首先先填满缓冲区,然后将整块数据传输给硬盘,并清空缓冲区,以备下一批输出使用。这被称为刷新缓冲区。但如果是通过键盘来输入的话,在这种情况下,C++程序通常在用户按下回车键时刷新输入缓冲区。
- 流文件的关系图
streambuf
类为缓冲区提供了内存,并提供了用于填充缓冲区,访问缓冲区内容,刷新缓冲区和管理缓冲区内存的类方法ios_base
类表示流的一般特征,如是否可读取,是二进制还是文本流等ios
类基于ios_base
,其中包括了一个指向streambuf
对象的指针成员ostream
类是从ios
类派生而来的,提供了输出方法istream
类是从ios
类派生而来的,提供了输入方法iostream
类是基于istream
和ostream
类的,因此继承了输入方法和输出方法fstream
类是从iostream
类派生而来的,而后者基于istream
和ostream
类,因此它继承了它们的方法
iostream
文件中的8个流对象(4个用于窄字符流,4个用于宽字符流)cin
对象对应于标准输入流。在默认情况下,这个流被关联到标准输入设备(通常是键盘)。wcin
对象与此类似,但处理的是wchar_t
类型。cout
对象与标准输出流对应。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。wcout
对象与此类似。cerr
对象与标准错误流相对应,可用于显示错误消息。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。这个流没有被缓冲,这意味着信息将被直接发送到屏幕,而不会等到缓冲区填满或新的换行符。wcerr
对象与此类似。clog
对象也对应着标准错误流。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。这个流被缓冲。wclog
对象与此类似。
使用cout
进行输出
-
write()
- 模板原型
basic_ostream<charT,traits>& wirte(const char_type *s,streamsize n);
write()
的第一个参数提供了要显示的字符串的地址,第二个参数指出要显示多少个字符。使用cout
调用write()
时,将调用char
具体化,因此返回类型为ostream &
- 一个例子
#include<bits/stdc++.h> using namespace std; int main() { const char* one="helloworld"; const char* two="guoshen"; int len=strlen(one); for(int i=1;i<=len;i++) { cout.write(one,i); cout<<endl; } cout.write(one,len+8); }
- 注意,
write()
方法并不会在遇到空字符时自动停止打印字符。
- 模板原型
-
进制转换(
hex(),dec(),oct()
)#include<bits/stdc++.h> using namespace std; int main() { int a=1234; cout<<hex<<a<<endl;//16进制 cout<<oct<<a<<endl;//8进制 cout<<dec<<a<<endl;//10进制 } /* output: 4d2 2322 1234 */
-
int width()
和int width(int i)
-
前者返回字段宽度的当前设置,后者设置字符宽度为i个空格,并返回以前的字段宽度
-
调整字段宽度
-
只会影响下一个项目,然后字段宽度将恢复为默认值
-
默认是右对齐
-
一个例子
#include<bits/stdc++.h> using namespace std; int main() { cout<<'#'; cout.width(12); cout<<12<<'#'<<24<<"#\n"<<endl; } /* output: # 12#24# */
-
-
fill(char a)
-
填充未被使用的字段
-
一直有效,直到被更改
-
一个例子
#include<bits/stdc++.h> using namespace std; int main() { cout<<'#'; cout.width(12); cout.fill('*'); cout<<12<<'#'<<24<<"#\n"<<endl; } /* output: #**********12#24# */
-
-
precision(int i)
-
设置浮点数的显示精度为i
-
默认精度为6位(但末尾的0将不显示)
-
一直有效,直到被更改
-
一个例子
#include<bits/stdc++.h> using namespace std; int main() { double pi=acos(-1.0); cout<<pi<<endl; cout.precision(2); cout<<pi<<endl; cout.precision(10); cout<<pi<<endl; } /* output: 3.14159 3.1 3.141592654 */
-
-
setf()
- 第一个原型
fmtflags setf(fmtflags);
fmtflags
是bitmask
类型的typdef
名,用于存储格式标记。- 在
ios_base
类中定义。 - 参数是一个
fmtflags
值,指出要设置哪一位。返回值是类型为fmtflags
的数字,指出所有标记以前的设置。如果打算以后恢复原始设置,则可以保存这个值。 - 格式常量表
式 常量 os_base::boolalpha 输入和输出bool值,可以为true或false os_base::showbase 对于输出,使用C++基数前缀(0,0x) os_base::showpoint 显示末尾的小数点 os_base::uppercase 对于16进制输出,使用大写字母,E表示法 os_base::showpos 在正数前面加+ - 第一个原型
-
-
第二个原型
fmtflags setf(fmtflags,fmtflags);
-
第一个参数和以前一样,也是一个包含了所需的
fmtflags
值。第二个参数指出要清除第一个参数中的哪一位。 -
调用
setf()
的效果可以通过unsetf()
消除 -
参数表
-
第二个参数 | 第一个参数 | 含义 |
---|---|---|
ios_base::basefield | ios_base::dec | 使用基数10 |
ios_base::basefield | ios_base::oct | 使用基数8 |
ios_base::basefield | ios_base::hex | 使用基数16 |
ios_base::floatfield | ios_base::fixed | 使用定点计数法 |
ios_base::floatfield | ios_base::scientific | 使用科学计数法 |
ios_base::adjustfield | ios_base::left | 使用左对齐 |
ios_base::adjustfield | ios_base::right | 使用右对齐 |
ios_base::adjustfield | ios_base::internal | 符号或基数前缀左对齐,值右对齐 |
使用cin
进行输入
- 流状态
成员 | 描述 |
---|---|
eofbit | 如果到达文件尾,则设置为1 |
badbit | 如果流被破坏,则设置为1;例如,文件读取错误 |
failbit | 如果输入操作未能读取预期的字符或输出操作没有写入预期的字符,则设置为1 |
goodbit | 另一种表示0的方法 |
good() | 如果流可以使用(所有的位都被清除),则返回true |
eof() | 如果eofbit被设置,则返回true |
bad() | 如果badbit被设置,则返回true |
fail() | 如果badbit或failbit被设置,则返回true |
rdstate () | 返回流状态 |
exceptions () | 返回一个位掩码,指出哪些标记导致异常被引发 |
exceptions(isostate ex) | 设置哪些状态将导致clear()引发异常;例如,如果ex是eofbit,则如果eofbit被设置,clear()将引发异常 |
clear(iostate s) | 将流状态设置为s;s的默认值为0(goodbit);如果(restate()&exception())!=0,则引发异常basic_ios::failure |
setstate(iostate s) | 调用clear(rdstate() | s).这将设置与s中设置的位对应的流状态位,其他流状态位保持不变 |
- I/O和异常
-
修改流状态涉及
clear()
和setstate()
,这都将会使用clear()
,修改流状态后,clear()
方法将当前的流状态与exceptions()
返回的值进行比较。如果在返回值(exceptions()
)中某一位被设置,而在当前状态中对应位也被设置,则clear()
将引发ios_base::failure
异常。 -
exceptions()
的默认设置为goodbit
,也就是没有引发异常,但重载的exceptions(iostate)
函数使得能够控制其行为:cin.exceptions(badbit)
-
一个例子
#include<bits/stdc++.h> using namespace std; int main() { cin.exceptions(ios_base::failbit); int x; int sum=0; try { while(cin>>x) { sum+=x; } } catch(ios_base::failure &bf) { cout<<bf.what()<<endl; } cout<<sum<<endl; } /* input: 10 20 30 pi output: basic_ios::clear 60 */
-
- 一些函数
get()
和getline()
和ignore()
-
get()
会将换行符留在输入流中,接下来的输入操作首先会是换行符,而getline()
不会 -
ignore()
的原型为istream & ignore(int =1,int =EOF)
该函数接受两个参数,一个是数字,指定要读取的最大字符数;另一个是字符,用作输入分界符。下面的函数调用读取并丢弃接下来的255个字符或直到到达第一个换行符
cin.ignore(255,'\n');
-
文件的输入和输出
- 二进制文件
-
二进制文件比较精确
-
要使用成员函数
read()
和write()
-
适用于结构体和不使用虚函数的类。
-
一个例子
#include<bits/stdc++.h> using namespace std; struct planet { char name[20]; double populatition; double g; }; const char *file="planets.dat"; inline void eatline(){while(cin.get()!='\n')continue;} int main() { planet p1; cout<<fixed<<right; ifstream fin; fin.open(file,ios_base::in|ios_base::binary); if(fin.is_open()) { while(fin.read((char*)&p1,sizeof p1)) { cout<<setw(20)<<p1.name<<": " <<setprecision(0)<<setw(12)<<p1.populatition <<setprecision(2)<<setw(6)<<p1.g<<endl; } fin.close(); } //add new data ofstream fout(file,ios_base::out|ios_base::app|ios_base::binary); if(!fout.is_open()) { cerr<<"Can't open"<<file<<endl; exit(EXIT_FAILURE); } cin.get(p1.name,20); while(p1.name[0]!='\0') { eatline(); cin>>p1.populatition; cin>>p1.g; eatline(); fout.write((char*)&p1,sizeof p1); cin.get(p1.name,20); } fout.close(); //show data fin.clear(); fin.open(file,ios_base::in|ios_base::binary); if(fin.is_open()) { while(fin.read((char*)&p1,sizeof p1)) { cout<<setw(20)<<p1.name<<": " <<setprecision(0)<<setw(12)<<p1.populatition <<setprecision(2)<<setw(6)<<p1.g<<endl; } fin.close(); } cout<<"Done\n"; return 0; } /* 输入请参照书本 */
-
随机存取
seekg()
和seekp()
函数-
seekg()
的原型(seekp()
与之类似)basic_istream<charT,traits>& seekg(streamoff,ios_base::seekdir); basic_istream<charT,traits>& seekg(streampos);
第一个原型定位到离第二个参数指定的文件位置特定距离(单位为字节)的位置;第二个原型定位到离文件开头特定距离(单位为字节)的位置
-
示例(假设
fin
是一个iftream
对象)fin.seekg(30,ios_base::beg);//30 bytes beyond the begining fin.seekg(-1,ios_base::cur);//back up one byte fin.seekg(0,ios_base::end);//go to the end of the file fin.seekg(112);//go to the 113 bytes
-
- 创建临时文件
tmpnam()
-
原型
char * tmpnam(char * pszName);
创建一个临时文件名,将它放在
pszName
指向的C-风格字符串中 -
常量
L_tmpnam
为文件名包含的最大字数,TMP_MAX
为该函数在不生成重复文件名的情况下最多可调用次数 -
一个例子
#include<bits/stdc++.h> using namespace std; int main() { char tmp[L_tmpnam]={'\0'}; for(int i=0;i<10;i++) { tmpnam(tmp); cout<<tmp<<endl; } }
-
-
内核格式化
-
C++库提供了
sstream
族,它们使用相同的接口提供程序和string
对象之间的I/O。 -
头文件
sstream
定义了一个从ostream
类派生而来的ostringstream
类。如果创建了一个ostringstream
对象,则可以将信息写入其中,它将存储这些信息。 -
ostringstream
类有一个名为str()
的成员函数,该函数返回一个被初始化为缓冲区内容的字符串对象 -
一个例子
#include<bits/stdc++.h> using namespace std; int main() { ostringstream outstr; string tmp; getline(cin,tmp); int cap; cin>>cap; outstr<<"hello world "<<tmp<<" has a capacity of "<<cap<<endl; string result=outstr.str(); cout<<result<<endl; }
-
istringstream
类允许使用istream
方法族读取istringstream
对象的数据,istringstream
对象可以使用string
对象进行初始化 -
一个例子
#include<bits/stdc++.h> using namespace std; int main() { istringstream in(string("guo shen is a cool boy!")); string tmp; while(in>>tmp) { cout<<tmp<<endl; } }
-
总之,
istringstream
和ostringstream
类使得能够使用istream
和ostream
类的方法来管理存储在字符串中的字符数据。