标签:
一、C++输入和输出概述
1.1、流和缓冲区
C++程序把输入和输出看作字节流。输入时,程序从输入流中抽取字节;输出时,程序将字节插入到输出流中。对于面相文本的程序,每个字节代表一个字符,更通俗地说,字节可以构成字符或数值数据的二进制表示。输入流中的字节可能来自键盘,也可能来自存储设备(如硬盘)或其他程序。输出流中的字节可以流向屏幕、打印机、存储设备或其他程序。流充当了程序和流源或流目标之间的桥梁。这使得C++程序可以以相同的方式对待来自键盘的输入和来自文件的输入。C++程序只检查字节流,而不需要知道字节来自何方。同理,通过使用流,C++程序处理输出的方式将独立于其去向。因此管理输入将包含两步:
*将流与输入去向的程序关联起来。
*将流与文件连接起来。
换句话说,输入流需要两个连接,每端各一个。文件端部连接提供了流的来源,程序端连接将流的流出部分转储到程序中(文件端连接可以是文件,也可以是设备)。同样,对输出的管理包括将输出流连接到程序以及将输出目标与流关联起来。
通常,通过使用缓冲区可以更高效地处理输入和输出。缓冲区是用作中介的内存块,它是将信息从设备传输到程序或从程序传输给设备的临时存储工具。
1.2、流、缓冲区和iostream文件
管理流和缓冲区的工作有点复杂,但iostream(以前为iostream.h)文件中包含一些专门设计用来实现、管理流和缓冲区的类。C++98版本C++I/O定义了一些类模板,以支持char和wchar_t数据;C++11添加了char16_t和char32_t具体化。通过使用typedef工具,C++使得这些模板char具体化能够模仿传统的非模板I/O实现。下面是其中的一些类:
*streambuf类为缓冲区提供了内存,并提供了用于填充缓冲区、访问缓冲区内容、刷新缓冲区和管理缓冲区内存的类方法;
*ios_base类表示流的一般特征,如是否可读取、是二进制流还是文本流等;
*ios类基于ios_base,其中包含了一个指向streambuf对象的指针成员;
*ostream类是从ios类派生而来的,提供了输出方法;
*istream类也是从ios类派生而来的,提供了输入方法;
*iostream类是基于istream和ostream类的,因此继承了输入方法和输出方法。
要使用这些工具,必须使用适当的类对象。例如,使用ostream对象(如cout)来处理输出。创建这样的对象将打开一个流,自动创建缓冲区,并将其与流关联起来,同时使得能够使用类成员函数。
补充:冲定义I/O
ISO/ANSI标准C++98对I/O作了两方面的修订。首相是从ostream.h到ostream的变化,用ostream将类放到了std名称空间中。其次,I/O类被重新编写。为成为国际语言,C++必须能够处理需要16位的国际字符集或更宽的字符类型。因此,该语言在传统的8位char(“窄”)类型的基础上添加了wchar_t(“宽”)字符类型;而C++11添加了类型char16_t和char32_t。每种类型都需要有自己的I/O工具。标准委员会被没有开发两套(现在为4套)独立的类,而是开发了一套I/O类模板,其中包括basic_istream<charT, traits<charT>>和basic_ostream<charT, traits<charT>>。traits<charT>模板是一个模板类,为字符类型定义了具体特性,如如何比较字符是否相等以及字符的EOF值等。该C++11标准提供了I/O的char和wchar_t具体化。例如,istream和ostream都是char具体化的typedef。同样,wistream和wostream都是wchar_t的具体化。例如,wcout对象用于输出宽字符流。头文件ostream中包含了这些定义。
ios基类中的一些独立于类型的信息被移动到了新的ios_base类中,这包括各种格式化常量,例如ios::fixed(现在为ios_base::fixed)。另外,ios_base还包含了一些老式ios中没有的选项。
C++的iostream类库管理了很多细节。例如,在程序中包含iostream文件将自动创建8个流对象(4个用于窄字符流,4个用于宽字符流)。
*cin对象对应于标准输入流。在默认情况下,这个流被关联到标准输入设备(通常为键盘)。wcin对象于此类似,但处理的是wchar_t类型。
*cout对象与标准输出流相对应。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。wcout对象与此类似,但处理的事wchar_t类型。
*cerr对象与标准错误流相对应,可用于显示错误消息。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。这个流没有被缓冲,这意味着信息将被直接发送给屏幕,而不会等到缓冲区填满或新的换行符。wcerr对象与此类似,但处理的是wchar_t类型。
*clog对象也对应着标准错误流。在默认情况下,这个流被关联到标准输出设备(通常为显示器)。这个流被缓冲。wclog对象于此类似,但处理的是wchar_t类型
*对象代表流——这意味着什么呢?当iostream文件为程序声明一个cout对象时,该对象将包含存储了与输出有关的信息的数据成员,如显示数据时使用的字段宽度、小数位数、显示整数时采用的计数方法以及描述用来处理输出流的缓冲区的streambuf对象的地址。下面的语句通过指向的streambuf对象将字符串"Hello world!"中的字符放到cout管理的缓冲区中:cout << "Hello world!";
总之,流的一端与程序相连,另一端与标准输出相连,cout对象凭借streambuf对象的帮助,管理者流中的字节流。
1.3、重定向
标准输入和输出流通常连接着键盘和屏幕。但很多操作系统(包括UNIX,Linux和Windows)都支持重定向,这个工具使得能够改变标准输入和标准输出。
二、使用cout进行输出
C++将输出看作字节流(根据平台的不同,可能是8位、16位或32位的字节,但都是字节),但在程序中,很多数据被组织成比字节更大的单位。例如,ing类型由16位或32位的二进制表示;double值由64位的二进制数表示。但在将字节流发送给屏幕时,希望每个字节表示一个字符值。因此,ostream类最重要的任务之一是将数值类型(如int或float)转换为以文本形式表示的字符流。也就是说,ostream类将数据内部表示(二进制位模式)转换为由字符字节组成的输出流(以后会有仿生移植物,使得能够直接翻译二进制数据)。为执行这些转换任务,ostream类提供了多个类方法。
2.1、重载的<<运算符
在C++中,与C一样,<<运算符的默认含义是按位左移运算符。但ostream类重新定义了<<运算符,方法是将其重载为输出。在这种情况下,<<叫做插入运算符,而不是左移运算符。插入运算符被重载,使之能够识别C++中所有的基本类型:
* unsigned char;
* signed char;
* char;
* short;
* unsigned short;
* int;
* unsigned int;
* long;
* unsigned long;
* long long(C++11);
* unsigned long long (C++11);
* float;
* double;
* long double;
对于上述每种类型,ostream类都提供了operator<<()函数的定义。因此,如果使用下面这样一条语句,而value是前面列出的类型之一,则C++程序将其对应于有相应的特征标的运算符函数:
cout << value;
2.1.1 输出和指针
ostream类还为下面的指针类型定义了插入运算符函数:
* const signed char *;
* const unsigned char *;
* const char *;
* void *。
C++使用指向字符串存储位置的指针来表示字符串。指针的形式可以是char数组、显式的char指针或用引号扩起来的字符串;并使用字符串中的终止空字符来确定何时停止显示字符。
对于其他类型的指针,C++将其对应于void *,并打印地址的数值表示。如果要获取字符串的地址,则必须将其强制转换为其他类型,例如:
int num = 12;
char * name = "Tom";
cout << # // 打印变量num的地址
cout << name;//将会打印字符串“Tom”
cout << (void *) name;//将会打印字符串“Tom”的地址
2.1.2 拼接输出
插入运算符的所有化身的返回类型都是ostream & 。也就是说,原型格式如下:
ostream & operator<<(type);
(其中,type是要显示的数据的类型)返回类型ostream &意味着使用该运算符将返回一个指向ostream对象的引用;函数定义指出,该引用指向用于调用该运算符的对象。换句话说,运算符函数的返回值为调用运算符的对象。这种特性使得能够通过插入来连接输出。
2.2 其他的ostream方法
除了各种operator<<()函数外,ostream类还提供了put()方法和write()方法,前者用于显示字符,后者用于显示字符串。
最初,put()的原型如下:
ostream & put(char);
当前标准与此相同,但被模板化,以适用于wchar_t。可以用类方法表示法调用它:
cout.put(‘W‘);
其中,cout是调用方法的对象,put()是成员函数。和<<运算符函数一样,该函数也返回一个指向调用对象的引用,因此可以用它拼接输出:
cout.put(‘‘A).put(‘B‘);
在原型合适的情况下,可以将数值型参数(如int)用于put(),让函数原型自动将参数转换为正确的char值。例如:
cout.put(65);//显示A
cout.put(66.3);//显示B
write()方法显示整个字符串,其模板原型如下:
basic_ostream<charT, traits> & write (const char_type* s, streamsize n);
write()的第一个参数提供了要显示的字符串的地址,第二个参数指出要显示多少个字符。使用cout调用write()时,将调用char具体化,因此返回类型为ostream &。
1 #include <iostream> 2 3 int main() 4 { 5 using std::cout; 6 using std::endl; 7 const char * state1 = "Florida"; 8 const char * state2 = "Kansas"; 9 const char * state3 = "Euphoria"; 10 std::size_t len = std::strlen(state2); 11 cout << "Increasing loop index:\n"; 12 int i = 0; 13 for (i = 1; i < len; i ++) { 14 cout.write(state2, i); 15 cout << endl; 16 } 17 18 cout << "Decreasing loop index:\n"; 19 for (i = (int)len; i > 0; i--) { 20 cout.write(state2, i) << endl; 21 } 22 cout << "Exceeding string length:\n"; 23 cout.write(state2, len + 5) << endl; 24 return 0; 25 } 26 27 输出结果: 28 Increasing loop index: 29 K 30 Ka 31 Kan 32 Kans 33 Kansa 34 Decreasing loop index: 35 Kansas 36 Kansa 37 Kans 38 Kan 39 Ka 40 K 41 Exceeding string length: 42 KansasEuph
从上面的程序可以看出,write()方法并不会在遇到空字符时停止打印字符,而只是打印指定数目的字符,即使超出了字符串的边界。
write()方法也可用于数值数据,可以将数字的地址强制转换为char *,然后传递给它:
#include <iostream> int main() { long val = 560031841; std::cout.write((char *) & val, sizeof(val)); return 0; } 输出结果: aha!
这不会将数值转换为相应的字符,而是传输内存中存储的位表示。例如,4字节的long值将作为4个独立的字节被传输。输出设备将把每个字节作为ASCII码进行解释。
2.3 刷新输出缓冲区
如果程序使用cout将字节发送给标准输出,情况将如何?由于ostream类对cout对象处理的输出进行缓冲,所以输出不会立即发送到目标地址,而是被存储在缓冲区中,直到缓冲区填满。然后,程序将刷新(flush)缓冲区,把内容发送出去,并清空缓冲,以存储新的数据。通常,缓冲区为512字节或其整数倍。当标准输出连接的是硬盘上的文件时,缓冲可以节省大量的时间。
如果实现不能在所希望时刷新输出,可以使用两个控制符中的一个来强行进行刷新。空字符flush刷新缓冲区,而控制符endl刷新缓冲区并插入一个换行符。这两个控制符的使用方式与变量名相同:
cout << "Hello world!" << flush;
cout << "Wait for me." << endl;
事实上,控制符也是函数。例如,可以直接调用flush()来刷新cout缓冲区:
flush(cout);
然而,ostream类对<<插入运算符进行了重载,使得下述表达式将被替换为函数调用flush(cout):
cout << flush;
因此,可以用更为方便的插入表示法来成功地进行刷新。
2.4 使用cout进行格式化
ostream插入运算符将值转换为文本格式。在默认情况下,格式化值的方式如下。
* 对于char值,如果它代表的是可打印字符,则将被作为一个字符显示在宽度为一个字符的字段中。
* 对于数值整型,将以十进制方式显示在一个刚好容纳该数字及负号(如果有的话)的字段中;
* 字符串被显示在宽度等于该字符串长度的字段中。
浮点数的默认行为有变化。下面详细说明了老式实现和新式实现之间的区别。
*新式:浮点类型被显示为6位,末尾的0不显示(注意,显示的数字位数与数字被存储时精度设置没有任何关系)。数字以定点表示法显示还是科学计数法表示,取决于它的值。具体来说,当指数大于等于6或小于等于-5时,将使用科学计数法表示。另外,字段宽度恰好容纳数字和负号(如果有的话)。默认的行为对应于带%g说明符的标准C库函数fprintf()。
*老式:浮点类型显示为带6位小数,末尾的0不显示(注意,显示的数字位数与数字被存储时的精度没有任何关系)。数字以定点表示法显示还是以科学计数法表示,取决于他的值。另外,字段宽度恰好容纳数字和负号(如果有的话)。
因为每个值的显示宽度等于它的长度,因此必须显式地在值之间提供空格;否则,相邻的值将不会被分开。
注意:并非所有的编译器都能生成符合当前C++标准格式的输出。另外,当前标准允许区域性变化。例如,欧洲实现可能遵循欧洲人的风格:使用逗号而不是句点来表示小数点。也就是说,2.54将被写成2,54 。区域库(头文件locale)提供了用特定的风格影响(imbuing)输入或输出流的机制,所以同一个编译器能够提供多个区域选项。本章使用美式格式。
(1)修改显示时使用的计数系统
ostream类是从ios类派生来的,而后者是从ios_base派生来的。ios_base类存储了描述格式状态的信息。例如,一个类成员中某些位决定了使用的计数系统,而另一个成员则决定了字段宽度。通过使用控制符(manipulator),可以控制显示整数时使用的技术系统。通过使用ios_base的成员函数,可以控制字段宽度和小数位数。由于ios_base类是ostream的间接基类,因此可以将其方法用于ostream对象(或子代),如cout。
注意:ios_base类中的成员和方法以前位于ios中。现在,ios_base是ios的基类。在新系统中,ios是包含char和wchar_t具体化的模板,而ios_base包含了非模板特性。
要控制整数以十进制、十六进制还是八进制显示,可以使用dec、hex和oct控制符。例如,下面的函数调用将cout对象的计数系统格式状态设置为十六进制:
hex(cout);
完成上述设置后,程序将以十六进制形式打印整数值,直到将格式状态设置为其他选项为止。注意,控制符不是成员函数,因此不必通过对象来调用。
虽然控制符实际上是函数,但它们通常的使用方式为:
cout << hex;
ostream类重载了<<运算符,这使得上述用法与函数调用hex(cout)等价。控制符位于名称空间std中。下面的代码演示了控制符的使用方法(注意,可以单独使用控制符,也可以将其作为一系列插入的部分):
#include <iostream> int main() { using namespace std; cout << "输入一个数字:"; int n; cin >> n; std::string str(5,‘ ‘); cout << "n" << str << "n*n\n"; cout << n << str << n*n << "(decimal)\n"; //设置成十六进制系统 cout << hex; cout << n << str; cout << n*n << " (hexadecimal)\n"; //设置为八进制系统 cout << oct << n << str << n * n << " (octal)\n"; dec(cout); cout << n << str << n * n << " (decimal)\n"; return 0; } 输出结果: 输入一个数字:12 n n*n 12 144(decimal) c 90 (hexadecimal) 14 220 (octal) 12 144 (decimal)
(2)调整字段宽度
标签:
原文地址:http://www.cnblogs.com/mupiaomiao/p/4730757.html