文章目录
流的特点:一维、单方向
Extractors: ****>>
- read a value from the stream
- overload the >> operator
Inserters: <<
- Insert a value into a stream
- overload the << operator
manipulators: (操纵器)
- change the stream state;
others
- some functions
Kinds of streams
1. text streams
(输入) 解析 / (输出)格式化
- deal in ASCII text
2. Binary streams(不以人的阅读为目的的文件) - 不经过解析和格式化的过程
Predefined streams
- cin
standard input - cout
standard output - cerr
unbuffered error output - clog
buffered error outpit
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jyn3bC4C-1657814432263)(2022-07-07-20-53-09.png)]
Defining a stream extractor
istream operatror>>(istream & is, T & obj) {
...
return is;
}
流、缓冲区和iostream文件
C++程序把输入和输出看做字节流。输入时,程序从输入流中抽取字节,输出时,程序将字节插入到输出流中。
流充当了程序和流源(文件或键盘等输入设备)或流目标(文件或显示器)之间的桥梁。C++只是检查字节流,使得程序处理输入输出与来源和方向相互独立,不需要关心不同的设备去向具体的底层实现的差别。
缓冲区作为设备和程序之间的媒介,可以起到平衡输入输出的处理速度差异的作用,提高效率。
流和缓冲区的管理工作交由一些专门设计的类来完成。
- ios_base类表示流的一般特征,如是否可读取,是二进制流还是文本流
- ios类基于ios_base,其中包括了一个指向streambuf类对象的指针成员
- streambuf类为缓冲区提供了内存,并提供了用于填充缓冲区、访问缓冲区内容、刷新缓冲区和管理缓冲区内存的类方法。
- istream和ostream类都是从ios类派生而来的,提供了输入和输出方法
- iostream是基于istream和ostream类,多重继承了这两个类的输入和输出方法。
创建这样的对象将打开一个流,自动创建缓冲区,并将其与流关联起来,同时使得能够使用类成员函数。
C++的iostream类库管理了很多细节,在程序中包含iostream文件将自动创建8个流对象(4个用于窄字符流,4个用于宽字符流)
cin,cout,cerr,clog \wcin,wcout,wcerr,wclog
使用cout进行输出
ostream类的最重要的任务之一数据编码格式的转换:即将数值类型(如int、float等计算机内部的数据表示格式)转换成文本形式表示的字符流(如ASCII码,显示器能直接显示的、给人看的字符数据格式)
ostream的类方法
- ostream重载了<< 运算符 (插入运算符) 使之能够识别C++中所有的基本类型。
- ostream类还为下面的指针类型定义了插入运算符
- const signed char*
- const unsigned char*
- const char*
- void* //其他类型的指针C++都将其对应于void*,并打印地址的数值表示
C++使用指向字符串存储位置的指针来表示字符串。指针的形式可以是char数组名、显式的char指针、或用括号引起的字符串。
因此,下面的所有cout语句都显示字符串
char name[20] = "hello world";
char *pn = "dejifeede";
cout << "hello"; //括号引起的字符串
cout <<name; //char数组名
cout << pn; //显式的char指针
方法使用字符串中的终止空字符(“\0”)来确定何时停止显示字符
重载 << 运算符
ostream类中的<<运算符重载的写法:
ostream & operator<<(T & obj)
{
...
return *this;
}
但是在ostream类中进行<<运算符重载是危险的,所以一般要在自定义的类中进行<<运算符的重载。使用友元来解决参数顺序的问题。
Creating a stream inserter
class T
{
friend ostream & operator<<(ostream & os, const T & obj);
}
ostream & operator<<(ostream & os, const T & obj){
...
return os;
}
put()方法和write方法
除了各种operator<<()方法外,ostream类还提供了put()方法和write方法,前者用于显示字符,后者用于显示字符串。
put方法:显示字符
//返回对象的引用,支持拼接输出
ostream & put(char);
cout.put('w');
cout.put('I'),put('t');
cout.put(65); //displaty the A character
cout.put(66.3);// 66.3 → 66 —→ B
wiite()方法:显示字符串的前n个字符
需要注意的是,write()方法并不会在遇到空字符时自动停止打印字符,而只是打印指定数目的字符
//函数模板原型
//使用cout调用write()时,将调用char具体化,因此返回类型为ostream&, 也支持拼接输出
basic_ostream<charT,traits>& write(const char_Type* s, streamsize n);
//使用演示
#include<iostream>
int main()
{
using std::cout;
using std::endl;
const char* str1 = "0123456789";
const char* str2 = "**********";
const char* str3 = "hello world";
const char* str4 = "niubi";
for (int i = 1; i <= std::strlen(str1); i++) //递增循环输出
{
cout.write(str1, i);
cout << endl;
}
cout << "-----------------"<<endl;
for (int i = std::strlen(str2); i >= 1; i--)//递减循环输出
{
cout.write(str2, i);
cout << endl;
}
cout << "-----------------" << endl;
cout.write(str3, std::strlen(str3) - 5).write(str4, std::strlen(str4))<<endl; //输出可以进行拼接
//将wirte()方法用于数值数据
long val = 560031841; //long数据占用4个字节传输,
cout.write((char*)&val, sizeof(long));
//将数字的地址强转为char*,传递给write()函数
//每个字节被翻译成ASCII码进行解释,因此在屏幕上,数值560031841将被显示为4个字符的组合
}
刷新输出缓冲区
由于ostream类对cout对象处理的输出进行缓冲,所以输出不会立即发送到目标地址,而是被存储在缓冲区中,知道缓冲区填满。然后,程序将刷新缓冲区,把内容发送出去,并清空缓冲区,以存储新的数据。通常,缓冲区为字节及其整数倍。
- 当标准输出连接的是硬盘上的文件时,缓冲可以节省大量的时间。将大量字符一次性写入硬盘的效率会更高。
- 对于屏幕来说,程序不必等到缓冲区被填满才进行刷新,例如,将换行符发送到缓冲区后,将刷新缓冲区。
另外,多数C+会在输入即将发生时刷新缓冲区。
cout << "dewfewf";
float num;
cin >> num;
因为下面有cin,程序期待输入这一事实,所以即使输出字符串中没有换行符,也会刷新缓冲区,立刻输出。
有两个控制符可以对缓冲区进行强行刷新
- 控制符flush刷新缓冲区
- 控制符endl刷新缓冲区,并插入一个换行符。
cout << "efgewfgw" << flush;
cout << "ferwerewf"<< endl;
用cout进行格式化
1. 修改计数系统 cout << hex;
使用控制符控制显示证整数时使用的计数系统,以十进制、十六进制、还是八进制可以显示可以使用dec,hex,oct控制符。
使用方式如下:
//控制符不是成员函数,不必通过对象来调用
hex(cout);
dec(cout);
oct(cout);
//或 ostream类重载了<<运算符,也可以这么使用控制符
cout << hex;
cout << dec;
cout << oct;
2.调整字段宽度 cout.width(5);
- 可以使用width成员函数将长度不同的数字放到宽度相同的字段中。
- width()方法只影响下一个项目,然后字段宽度将恢复为默认值
- C++总会增长字段,以容纳数据,因此这种值适用于所有的数据。
- 右对齐是默认的
使用示例:
int width(); //用来返回当前字段宽度的设置
int width(int i); //用来设置字段段杜,并返回之前的字段宽度
#include <iostream>
int main()
{
using std::cout;
int old = cout.width(); //默认宽度为0
cout << "default field width = " << old << ":\n";
cout.width(5);
cout << "N" << ':';
cout.width(8);
cout << "N * N" << ":\n";
for (long i = 1; i <= 100; i *= 10)
{
cout.width(5);
cout << i << ':';
cout.width(8);
cout << i * i << ":\n";
}
// std::cin.get();
return 0;
}
3. 填充字符 cout.fill(‘*’);
- cout.fill(‘*’);将使用 * 来填充字段中未使用的部分
- 默认情况下使用空格填充
- 注意:与字段宽度不同的是,新的填充字符将一直有效,直到更改它为止。
4. 设置浮点数的显式精度 cout.precision(2);
浮点数精度的含义取决于输出模式
- 在默认模式下,指的是显式的总位数
- 在定点模式和科学模式下,指的是小数点后面的位数
- cout.precision(2);设置精度为2
- 与fill()类似,新的精度设置将一直有效,直到被重新设置。
- C++的默认精度为6位,但末尾的0将不显示
setf()方法
ios_base类提供了一个setf()函数,能够控制多种格式化特性。这个类还提供了多个常量,可用作该函数的参数。
setf()成员函数有两个原型:
//fmtflags是bitmask类型的typedef名。bitmask是一种用来存储各个位值的类型,每一位都是可以单独访问的,都有自己的含义。iostream软件包使用bitmask来存储状态信息。
fmtflags setf(fmtflags);
fmtflags setf(fmtflags, fmtflags);
要点:
- ios_base类位于std名称空间中
- 这些格式常量都是在ios_base类中定义的,使用时需要加上作用域解析符
- 修改将一直有效,直到被覆盖位置
- setf()是ios_base类的一个成员函数,由于这个类是ostream的基类,因此可以用cout对象来调用该函数
setf()一个参数设置格式,并返回以前的设置
一个参数setf()使用的格式常量:
- ios_base::boolalpha 输入和输出bool值
- ios_base::showbase 输出使用基数前缀0,,0x
- ios_base::showpoint 显示末尾的小数点
- ios_base::uppercase 对于16进制输出,使用大写字母,E表示法
- ios_base::showpos 在正数前面加+号显示
使用示例:
#include <iostream>
int main()
{
using std::cout;
using std::endl;
using std::ios_base;//预编译指令
int temperature = 63;
cout.setf(ios_base::showpos); // 加+号
cout << temperature << endl;
cout << std::hex << temperature << endl; // 使用hex控制符
cout.setf(ios_base::uppercase); // 16进制大写
cout.setf(ios_base::showbase); // 使用基数前缀
cout << "or\n";
cout << temperature << endl;
cout << true ; //输出1
cout.setf(ios_base::boolalpha); //输出bool值true
cout << true << "!\n";
return 0;
}