第17章:输入、输出和文件


流的特点:一维、单方向
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的类方法

  1. ostream重载了<< 运算符 (插入运算符) 使之能够识别C++中所有的基本类型。
  2. 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);

浮点数精度的含义取决于输出模式

  • 在默认模式下,指的是显式的总位数
  • 在定点模式和科学模式下,指的是小数点后面的位数
  1. cout.precision(2);设置精度为2
  2. 与fill()类似,新的精度设置将一直有效,直到被重新设置。
  3. 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;
}
setf()原型接受两个参数,并返回以前的设置
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值