输入、输出和文件
架构
- ios_base类表示流的一般特征,如是否可以读写,二进制还是文本流。
- ios类基于ios_base ,其中包括一个指向streambuf对象的成员
- streambuf类为缓冲区提供了内存,并提供了一些填充缓冲区,访问缓冲区内容,刷新缓冲区和管理缓冲区内存的方法。
- ostream类和istream类都继承与ios类,分别提供了输出方法和输入方法
- iostream类基于ostream类和istream类,继承了输出方法和输入方法
- 开发模板,应对与不同的字符集,例:istream 和ostream使char 的具体化的typedef,wistream和wostream是wchar 具体化的 typdef
iostream
概述
- 程序包含iostream将会创建8个流对象(4个窄字符流,4个宽字符流)
- cin对象对应标准输入流,默认关联到标准输入设备(键盘)wcin类似
- cout对象对应标准输出流,默认关联到标准输出设备(显示器)wcout类似
- cerr对象对应标准错误流,显示错误信息,默认关联到标准输出设备(显示器),其没有被缓冲,意味着其将直接发送给屏幕。wcerr类似
- clog对象也对应标准错误流,也被关联到标准输出设备,这个流被缓冲。
- 对象代表流,当iostream 声明一个cout对象,盖对象将包含存储与输出有关的信息的有关数据成员。如字段宽度,小数位数,显示整数采用的计数方法以及描述处理输出缓冲区的streambuf对象的地址。
重定向
在命令控制太运行程序,并更改输入输出默认流

test.exe是一个数字符程序,words.txt是一个有许多英文符的txt文件,而a.txt则是输出文件,
< word.txt 将cin 的默认输入流从键盘变为文件,> a.txt将使输出由原来的屏幕重定向为文件(磁盘)
注:
- 标准输出重定向并不会影响cerr或clog,而只改变了cout
- 这里建议使用cmd而不是powershell,事实证明powershell对操作符 < 仅为未来保留而不解释执行。

将test.exe程序中循环输入添加一个限制条件,当字符数大于10时,将想cerr发送错误报告并跳出循环。可见cerr并不没有被重定向为文件,其标准输出流仍然关联在屏幕上。
cout
概述
在C中<< 被用作左移运算符,其将数的二进制位左以若干位。在C++中它被重载为输出,当然对于各种内置类型,它将被多次重载,以便可以正确的处理各种内置类型。
const char* a = "tee";
cout << (void *) a << endl;
利用强转来输出字符串的地址。
cout<<"we have "<<count <<" words"<<endl;
ostream & operator << (type);
利用返回ostream & 的引用来拼接输出。
其他ostream 方法
put()
其用于显示字符
cout.put('a').put('b').put('c');
//abc
cout.put(66.6);
//截断式输出 B 隐式转型
同<<一样,put()函数也返回一个cout对象
write()
其用于显示字符串
cout.write("asdfafd", 4);
//asdf
write()#1为字符串地址,#2为显示的长度。同<<一样,write()函数也返回一个cout对象.注意:write()不会遇到空字符停止打印,而是会严格的打印n个字符
long val = 560031841;
cout.write((char*)&val, sizeof(val));
强转打印val(long)
刷新输出缓冲区
在屏幕输出时,程序不必等到缓冲区被填满。例如,将换行符发送到缓冲区,将刷新缓冲区,多数C++实现都会在输入即将发生时刷新缓冲区。
cout << "Enter a number:";
float num;
cin >> num;
因为程序期待一个输入并立即刷新输出缓冲区的特性,因此可以使用户直观的获得要输入一个数的信息。
刷新缓冲区
flush用于刷新缓冲区
cout<<flush;
flush(cout);//#2
#2 对于flush,它不仅是一个控制符,也是一个函数.实际上<<是对于操作符重载并调用了flush(cout)
endl用于刷新缓冲区,并添加一个换行符
cout<<endl;
cout格式化
整数计数系统
int a(1024);
hex(cout);//16进制
cout << "hex:" << a << endl;//400
dec(cout);//10进制
cout << "dec:" << a << endl;//1024
oct(cout);//8进制
cout << "oct:" << a << endl;//2000
也可以使用重载操作符进行设置
int a(1024);
cout<<hex << "hex:" << a << endl;
cout<<dec << "dec:" << a << endl;
cout<<oct << "oct:" << a << endl;
hex(ostream &) 将输出流输出格式设为16进制
dec(ostream &) 将输出流输出格式设为10进制
oct(ostream &) 将输出流输出格式设为8进制
调整字段宽度
cout.width();
cout.width(int i);
width(i)将cout的宽度设置为i,并返回原宽度,且设置的宽度只对下一个cout输出有效。
注意:如果设置的宽度小于数字表示的宽度,将会增宽字节,显示数据的内容要比整洁更有用。
- 默认宽度为0,因为C++会增宽字节
- 默认对齐方式为右对齐
- 填充字符默认为空白
int a(1024);
cout.width(12);
cout.fill('*');
cout << a;//********1024
fill(char),更改填充字符
- 与更改字符宽度不同,填充字符的更改将会一直有效,直到下一次更改。
浮点数的显示精度
精度,在默认模式下,指显示的总位数。在定点模式和科学模式下,精度指小数点位数。默认精度为6为。
double a = 1.0 / 9.0;
double b = 1.111000;
cout.precision(2);
cout << a << endl << b;
//0.11
//1.1
precision()将精度重新设置为一个数,并且与fill相同,一直持续到下一次设置。
打印末尾的0和小数点
cout.setf(ios_base::showpoint);
fmtflags setf(fmtflags );返回值的类型为fmtflags的数字,指出所有标记以前的设置。
fmtflags 是bitmask类型的
| 常量 | 含义 |
|---|---|
| ios_base::boolalpha | 输入和输出bool值,可以为true或false |
| ios_base::showbase | 对于输出,使用C++基数前缀(0,0x) |
| ios_base::showpoint | 显示末尾的小数点 |
| ios_base::uppercase | 对于16进制输出,使用大写字母,E表示法 |
| ios_base::showpos | 在正数前面加上+ |
- 注意仅当计数为10时才使用加号,16进制于8进制,C++视为无符号的
fmtflags setf(fmtflags ,fmtflags);#1为设置位,#2位清除位
注:#2类似掩码,将其有效位设为111在通过运算使,成员中某些特定位置清零。
ios_base::fmtflags old = cout.setf(ios_base::left,ios_base::adjustfield);
cout.setf(old, ios_base::adjustfield);
- 可以使用上面的技巧进行复位。
- 精度,对于默认的浮点表示,则是只显示有效数字,而对于定点模式和浮点模式则是小数点后有效数字
unsetf()
unsetf(ios_base::showpoint);
unsetf(ios_base::boolalpha);
unsetf(ios_base::floatfield);#3
#3 将使模式从浮点模式或定点模式切换至默认模式
也可以这样setf(0,ios_base::floatfield);
控制符
使用setf()还要记忆许多的常量,因此提供了控制符,如oct,dec,hex,left等等之类的,其本质只是重载了<<操作符,并且调用了setf()函数,表格p751
iomanip
setprecision() 精度
setfill() 填充
setw() 宽度
三个函数都接受一个参数,并且也是控制符,可以被cout连接
cout << setfill('*') << setw(11);
cin
控制符
int a;
cin >> hex >> a;
cout << a;
//A
//10
hex控制符,将输入理解位16进制输入,同理还有dec,oct
拼接
cin>>name>>fee>>group;
返回cin的引用,使得cin输入可以拼接连输
检查输入
不同的抽取运算符检查输入流的方法是相同的,它将会跳过空白(空格,换行符,制表符),包括单字符也是如此。如果,输入流没有满足程序的值,那么cin 将不回修改变量的值,并返回0.
流状态
p756
clear()与setstate()的区别,clear(s)是将流状态直接设置位s,而setstate(s)则是只设置s属于它那个位,与其他标志位无关。
exceptions() 返回当前异常的掩码
exceptions(isostate ex) 将ex添加到异常检测位中
clear(isostate s) 将流状态设置为s,同时检测当前设置的流状态与异常掩码之间的,如果有,则引发ios_base::failure异常
cin.exceptions(ios_base::eofbit);
try
{
cin.clear(ios_base::eofbit);
}
catch (const std::ios_base::failure & e)
{
cout << e.what();
}
//ios_base::eofbit set: iostream stream error
上述代码,先cin.exceptions()对异常触发的状态进行修改,再调用clear(),设置当前流状态为eofbit,从而使得触发了异常。
- clear() 是将流状态与设置掩码进行与运算(&),若不为0则触发异常,其默认值为goodbit
- exceptions()的默认设置为goodbit,即不会触发异常。
- rdstate()返回当前流状态码
- cin>>input 返回一个cin引用,其可以被隐式的转换bool型,只用在流状态良好时,其才返回1.
- 设置的流状态将会对接下来的输入都产生影响,即一次输入失败,流状态被设置,后续输入将不会进行。
- 使用cin.clear()清除流状态,但是不合法的输入仍然留在输入队列中,所以可以使用while(cin.get()!=’\n’)来清除输入队列。或者isspace(int _val)来判断输入字符是否为空字符。
- fail()当eofbit为1时也为0,当failbit为1时为1,历史原因已经被修复了。
其他istream方法 - get(char &)与get(void)可以不跳过空字符,而普通的>>运算符将会抽取空字符,get(char & )返回的为istream对象,因此可以拼接,get(void)将会返回其字符的整型,get(void)如果读取到文件尾,将会返回EOF常量
- get(char*,int,char)和getline(char*,int,char)在默认情况下读取整行而不是一个单词,char* 为一块内存块,int为限制长度,长度中有一位时‘\n’,即n要比你的字符有效长度(不包括’\n’)大1,char为终止符.
- get() 与getline()的主要区别是getline()丢弃了换行符,而对于get()则是换行符仍留在输入队列中,对于指定的分界符也是如此,getline()丢弃,get()保留
char a[10];
cin.get(a,8);
cout << a;

- ignore(int =1,int= eof);其默认读取1个字符或者读取eof,并将它们丢弃。
意外字符串输入 - get(),getline()同其他一样,其遇到文件尾将会设置eofbit,遇到流被破坏,设置badbit.
- getline()在意外出现超过限制字符数,将会设置failbit,get()则不会设置failbit
其他istream方法
- read() 读取指定的数目的字符数,并存储到指定位置。与getline()与get()不同,其不会在输入后加入空值字符。
- peek()返回输入流中的下一个字符,但是不抽取。但是这个函数似乎有些鸡肋,因为strlen()完全可以取代其工作,而且速度要快。
- putback(char & )其将一个字符插入到输入队列的首位。
文件输入和输出
简单的文件IO
关联
ofstream fout;
fout.open("a.txt");
ofstream fout("a.txt");
读取文件同写入文件差不多
close() 主动关闭文件,其实对象过期时,到文件的连接将会自动关闭,close()关闭连接,并不会删除流,只是断开了流和文件的连接,其管理的缓冲区仍然存在,fin对象也存在。调用close() 可以使得缓冲区的内容写入到文件。
cout << "enter your filenam: ";
string filename;
cin >> filename;
ofstream fout(filename.c_str());
fout << "this is a file,and the name of file is " << filename;
fout.close();
ifstream fin(filename.c_str());
char a[1024];
fin.getline(a, 1024);
cout << a;
fin.close();
流状态位
同cout,cin一样,毕竟都是从ios_base类中继承。
failbit位:试图打开一个不存在的文件进行输入,
ifstream fin("1234.txt");
if (fin.fail())
{
...
}
ifstream fin("1234.txt");
if (!fin)
{
...
}
fin同cin,cout一样,在需要转换位bool值时,将会转换位bool值.fin.good()位false,则fin对象将会被转换false.
is_open()检测文件是否被正确打开。其优于上面的地方在于,它可以检测到一些微妙的问题,如是否以正确的文件模式打开。good()函数也可以检测到这类错误。
打开多个文件:
- 创建多个流对象
- 创建一个流对象并多次关联,比上一种方法节约计算机资源
命令行处理技术
int main(int argc,char * argv[]);
argc为命令行中参数个数,包括命令名本身。
argv为指针数组
wc report1 report2 report3
argc=4
argv[0] 指向 wc argv[1] 指向 report1
如下例:
int main(int argc ,char * argv[])
{
for (int i = 0; i < argc; i++)
{
cout << argv[i] << endl;
}
}

注意:在重新关联文件时,要注意编译器是否回清除流状态,如果没有,则需要调用fin.clear(),在VS中回自动清除,甚至忘记关闭文件。
文件模式
文件模式描述的文件将被如何使用,读、写、追加。
ifstream fin("test.txt",mode1);
ofstream fout;
fout.open("test.txt",mode2);
mode类似fmtflags也是一种掩码类型。预定义常量表格p774
ofstream fout;
fout.open("words.txt", ios_base::app);
fout << "\nappend" << endl;
open()使用了追加模式
- ifstream open()模式默认值为ios_base::in
- ofstream open()模式默认值为ios_base::out|ios::trunc
- fstream 不提供默认值,因此在创建fstream时,要指定模式
- ios_base::ate将指针放到文件尾,而ios_base::app是允许数据添加到文件尾
- ios_base::app允许数据添加到文件尾,但是对于文件前面的部分则是只读x
long a = 78;
long b;
ofstream fout;
fout.open("words.txt", ios_base::out|ios_base::binary);
fout.write((char*)&a, sizeof(a));
fout.close();
ifstream fin("words.txt", ios_base::in | ios_base::binary);
fin.read((char*)&b, sizeof(b));
cout << b;
- write() 和 read()对二进制文件进行读写
随机存取
fstream finout("words.txt",ios_base::in|ios_base::out|ios_base::binary);
finout.seekg(25, ios_base::beg);
string word;
finout >> word;
cout << word;
seekg(streamofff,ios_base::seekdri )将输入指针调到离文件开头25个字节,文件字节是从0开始编号的,第25个字节,本身也算。
seekg(steampos)绝对位置,此位置以开头作为参考量
ios_base::beg 开头
ios_base::cur 当前
ios_base::end 结尾
tellg()将返回当前指针的位置,其以开头作为偏移量。
内核格式化
其实就是用缓冲区来模仿输入输出,但是操作的对象是字符串。让字符串也可以像文件或者输入行一样被输入输出。
sstream
ostringstream outstr;
double price = 380.0;
const char* p = "for a copy of";
outstr.precision(2);
outstr << "price: " << price << endl;
outstr << p;
string result = outstr.str();
cout << result;
str()将返回当前缓冲区中的字符,且不会被因函数的调用被清除,即可以叠加。
string a;
istringstream instr("tdd");
instr >> a;
cout << a;
//tdd
instr可以使用string 对象进行初始化。
本文深入介绍了C++中的I/O流,包括iostream库的基本概念,如cin、cout、cerr和clog的使用。详细讨论了流的重定向、输出格式控制、错误处理和文件输入输出。还涵盖了流状态位、缓冲区管理和文件模式等核心概念,以及如何进行命令行参数处理。通过对标准输出的控制,如字段宽度、填充字符和精度设置,实现了更灵活的数据输出。同时,文章也讲解了如何处理文件输入,包括打开模式、随机存取和异常处理。最后,提到了内核格式化,即通过stringstream实现字符串的输入输出。
433

被折叠的 条评论
为什么被折叠?



