C++将输入和输出看作字节流,输入时,程序从输入流中抽取字节,输出时,程序将自己插入到输出流中,流充当了程序与流源或流目标之间的桥梁,也就是说C++通过流与硬件,文件相关联,流赋予了C++程序访问其他硬件或存储文件的能力,当我们想要访问某种硬件或者文件时,我们只需使用相关的流就可以啦!
文件,硬盘读取字节的速度是很慢的,而程序从内存中读取字节的速度很快,为了匹配两种不一样的速度,流采用缓冲区来匹配,在程序从键盘输入时,尽管读取速度与内存相同,但通过缓冲区,可以帮助用户纠正输入,只有用户敲下回车时,才会开始读取; 这也就意味着,输入流迭代器需要执行减操作,输出流迭代器只需执行+操作;
缓冲区和流是通过类来实现的,ios类的成员包括一个streambuf类来管理输入输出的缓冲区的内存,ios类派生出ostream类和istream类,再由iostream类继承两者的输出输出方法,
1.streambuf类提供了填充缓冲区,访问缓冲区,刷新缓冲区和管理缓冲区的方法,它包含在ios类中,
2.ostream提供输出方法,比如cout,istream提供输入方法,比如cin 从 键盘输入与键盘关联;fistream类表示从文件输入,与磁盘空间相关联,fostream类表示向磁盘空间输出,也与磁盘相关联,
3.ios_base表示流的一般特征,例如是二进制还是文本流等,是否可读,可写等,ios类基于ios_base类;
4.oetream,istream都位于标准名称空间std中。
重定义IO:
istream,ostream都是针对char类型的,即他将每个子集都看作char,而对于wchar_t,则需要wistream,wostream类,比如wcout用于输出宽字符流,他将两个字节看作一个wchar_t类型,而其他类型,比如图片,纯音乐,则还是保持istream和ostream;在C++11中,每种类型都有自己的io工具,而不是说需要我们重载,重载其实也是将由io方法本身就支持的基本类型组成的复合数据类型分拆输入,对于io方法不支持的类型比如wchar_t,char16_t就需要重新定义io方法;
对象代表流:当iostream文件为程序声明cout对象时,cout对象将包含存储了与输出有关的信息成员,如字段宽度,小数位数,计数方法等,即以什么样的方式输出什么样的对象;这是通过strembuf来实现的;
流的一端是显示器,键盘,另一端是程序,重定向意味着改变其中一端,通过使用重定向运算符来做到,输入重定向(<),输出重定向(>)但这最好不要;就是用相关的类就行了;
cout的输出会先存储到缓冲区,知道缓冲区填满,然后程序会刷新缓冲区,将内容发出去并清空缓冲区,比如一次性写入硬盘内,而当对象是显示器时,就不用等待缓冲区存满,即可清空缓冲区,一般来说使用endl,或者flush就可以刷新缓冲区,
cout<<flush;
flush(cout);
使用cout进行格式化(对底层进行解释):
ostream类的插入方法将数值转换为文本格式,即格式化,最终都显示为字段;对于字符和整形,字符串将会原样转化,对于浮点数,由于精度的问题,会涉及到转化后的字段长度,分为两种:
新式:浮点数小数点后显示6位,显示的字段是科学计数还是定点表示取决于它的值,当指数大于6或小于-5时,用科学计数法,
老式:数字最终是定点表示还是科学计数将完全取决于它的值,即它一开始是用定点表示还是说科学计数,而与他的小数点后几位无关;
不论哪种,都不会显示末尾的0。
修改显示时使用的技术系统:
继承关系:ostream->ios->ios_base,ios_base类存储了描述格式状态的信息,通过修改这些接口,将改变cout对于数字的输出格式(oct,hex,dec),输出长度;
输出格式:对于整数只有三个变量:hex,oct,dec;
int n = 200;
cout << hex;
cout << n << " ";
cout << n * n << "(hexadecimal)\n";//以16进制的方式输出
cout << oct << n << " " << n * n << "(octal)\n";//以八进制的方式输出;
dec(cout);
cout << n << " " << n * n << " (decimal)\n";//以十进制的方式输出
这些方法是在ios_base里的,由于ostream是他的派生类,可以用于ostream的方法;如cout;
调整字段宽度:
成员函数width()可以让输出对齐,注意是右对齐,但他只能影响一次,因此需要使用for循环来保证都对齐,然后用空格填满左侧;
int width();
int width(int i);
第一种格式返回字段宽度的当前设置,第二个将字段宽度设置为i,并返回以前的字段宽度,以便恢复宽度时使用,但输出字符超过字段宽度时,CPP将增加宽度与字段宽度相等,字段宽度小于设置宽度时,使用填充字符来填满,一般来看程序可以使用width()来对齐标题和数据,
修改填充字符:
默认情况下是空格,可以使用fill函数来改变:
cout.fill('*');不同于width(),新的填充字符将一直有效,
设置浮点数的显示精度:
cout.precision(int i);在一般情况下,它指的是浮点数的总位数,
float price1=1.9+8.0/9.0;
cout.precision(2);
结果为2.8;不设置的话就是默认为六位,结果为2.78889;
在定点模式和科学计数的情况下指的是小数点后的位数;
setf()格式函数:
setf()函数可以控制多种格式化特性;ios_base里有多个格式常量来控制格式;
如setf(ios_base::showpoint);将显示末尾的小数点;
ios_base类有一个受保护的数据成员,其中各位分别控制格式化的各个方面,如计数系统,是否显示末位0;每个位相当于电路里的一个开关,来控制计算机硬件,这里的位也叫做标记,如之前的hex,oct,dec就是三个标记位开关,而setf()提供了另一个改变标记的方法;
setf()第一个原型如下:
fmtflags setf(fmtflags);
fmtflags是bitmask类型的typedef,用于存储格式标记,该方法用于设置哪一位标记,返回值位以前的标记值,实际调用时,对于格式的设置利用常量就可以了:
ios_base::boolalpha 输入输出bool值(直接输出true或false,否则就是0或1)
ios_base::showbase 对于输出使用CPP前缀(0,0x)
ios_base::showpoint 显示末尾小数点;
ios_base::uppercase 对于16进制输出,显示大写字母;
ios_base::showpos 在正数前加+(指十进制,八进制和16进制不显示)
setf()第二个原型如下:
fmtflags setf(fmtflags,fmtflags);
此重载用于设置多位控制的格式选项,第一个参数和以前一样,指出需设置的fmtflags值,第二个是指出要清除第一个参数中的哪些位,这些的本质都是在改变ios_base里控制格式的数据成员的比特位,该位位于电路中可以改变输出时的电路走向,来控制输出,因此有时改变输出就需要不止一个位,比如假设第三位值为1表示以10为基数,第四位值为1表示以16为基数,那么要使得基数为16就要将四位置为1,三位以及其他位置为0(清除位),因此第二个参数实际上是一堆参数的集合,ios_base类也定义了这些常量:
具体应用:
cout.setf(ios_base::hex,ios_base::basefield);
他与
cout<<hex;
作用相同
该表是三组格式标记,每组标记都由一个可用作第二参数的常量和两三个可用作第一参数的常量组成,第二参数负责清除一批相关位,第一参数将其中一位设置为1,例如要选择左对齐,可以将ios_base::left作为第一参数,ios_base::adjustfield作为第二参数,内部对齐表示将符号或基数前缀放在字段左侧,余下的数字放字段右侧,比如正负号放左侧,数字放右侧。
cout.setf(ios_base::left,ios_base::adjustfield);
定点表示法意味着使用格式123.4表示浮点值,不管数字长度如何,科学计数法意味着使用1.23e04,不考虑数字长度,在C++标准中,定点表示法和科学表示法有下面两个特征:
精度指小数位数,而不是总位数;
显示末尾的0
C++默认模式是%g,定点表示法是%f,科学表示法是%e,
setf()函数返回的是修改表记位之前的值,因此要想恢复之前的值,可以用fmtflags变量来接收函数返回值:
ios_base::fmtflags old = cout.setf(ios_base::left,ios_base::adjustfield); cout.setf(old,ios_base::adjustfield);
setf()函数的效果可以通过unsetf(fmtflags mas)消除,setf()将对应位置为1,unsetf()将对应位置为0;
cout.setf(ios_base::showpoint);//打印小数点
cout.unsetf(ios_base::showpoint);//不打印小数点;
对于浮点数默认模式,系统工作原理位:当定点表示和科学表示都被设置或者没有位被设置时使用默认模式:
cout.setf(0,ios_base::floatfield);//都不设置,并且全部归0,则切换至浮点数
的默认模式
cout.unsetf(ios_base__floatfield);该参数可以直接切换到浮点数的默认模式
使用setf(),unsetf()是对用户不友好的,C++直接使用多个控制符,能够调用setf(),就像我们cout<<hex一样去定义这些位,C++给出了相应的标准控制符:
cout<<left<<fixed;cout<<noboolalpha;->unset(ios_base::noboolalpha);
头文件iomanip:
iostream类来改变输出格式还是有点复杂了,因此C++在头文件iomanip中提供了一些其他控制符,3个最常用的控制符分别是setprecision(),setfill(),setw();分别用来设置精度,填充字符,字段宽度,他们作为控制符与上面的一样,是可以在cout<<后边的,它可以更为方便的多次修改格式;
cout << setw(6) << "M" << setw(14) << "square root" << setw(15) << "forth root\n";
double root=20;
for (int n = 10; n <= 100; n += 10)
{
root = sqrt(double(n));
cout << setw(6) << setfill(' ') << n << setfill('*') << setw(12) << setprecision(6) << root << endl;
}
这样可以生成几乎完全对齐的列,而且更为方便;
使用cin进行输入
cin对象将标准输入作为字节流,通过键盘来生成字节流,cin对象根据对象的类型,将字符序列转换为所需的类型,格式:
cin>>value_holder;value_holder是变量,引用或解引用的指针,cin转换的方式取决于value_holder的数据类型;这种转换我们成为格式化,即将一种类型转换为另一种类型,对于cin,也可以使用hex,oct,dec,将指定的字段输入解释为16进制,8进制和十进制,如0x12被解释为十六进制的12或者八进制的18,ff解释为十进制的255;
cin>>如何检查输入
cin并不会字符流中的所有字段进行写入,他会执行检查操作,一般来讲太会跳过空格,换行和制表符,对于单字符模式也是如此,跳过空白直到遇到非空白字符,但c语言的字符输入函数就不一样了,对于单字符模式(类型为char)将读取字符,其他模式下>>运算符将读取一个指定类型的数据,即从非空白字符开始到第一个不匹配的字符之间的全部内容,如果输入没有满足类型的值,cin的迭代器将不会移动,也不修改变量的值,并返回0;v