C++学习-入门到精通【11】输入/输出流的深入剖析

news2025/6/2 8:16:47

C++学习-入门到精通【11】输入/输出流的深入剖析


目录

  • C++学习-入门到精通【11】输入/输出流的深入剖析
    • 一、流
      • 1.传统流和标准流
      • 2.iostream库的头文件
      • 3.输入/输出流的类的对象
    • 二、输出流
      • 1.char* 变量的输出
      • 2.使用成员函数put进行字符输出
    • 三、输入流
      • 1.get和getline成员函数
      • 2.istream的成员函数peek、putback和ignore
      • 3.类型安全
    • 四、使用read、write和gcount的非格式化的I/O
    • 五、流操纵符简介
      • 1.整数流的基数:dec、oct、hex和setbase
      • 2.浮点精度
      • 3.位宽(width和setw)
      • 4.用户自定义输入流操纵符
    • 六、流的格式状态和流操纵符
      • 1.尾数零和小数点(showpoint)
      • 2.对齐(left、right和internal)
      • 3.内容填充(fill和setfill)
      • 4.整数流的基数
      • 5.浮点数、科学计数法和定点小数记数法(scientific、fixed)
      • 6.大写/小写控制(uppercase)
      • 7.指定布尔类型(boolalpha)
      • 8.通过成员函数flags设置和重置格式状态
    • 七、流的错误状态


一、流

C++的I/O是以一连串的字节流的方式进行的。在输入操作中,字节从设备(例如,键盘、磁盘驱动器、网络连接等)流向内存。在输出操作中,字节从内存流向设备(例如,显示屏、打印机、磁盘驱动器、网络连接等)。入:表示进入内存;出:表示离开内存。以内存为参照物。

这里的流是一个抽象的概念,表示一个连续的数据序列。对于上面的不同设备而言,它们处理的数据格式其实并不相同,所以我们使用一个流的抽象概念将数据的生产者和消费者解耦(生产者只需将生成的数据给到流,之后输出到各种设备的不同操作由流来决定;反之亦然。)这就使得程序只需关注数据本身,不需要关注到数据如何流动到不同设备之中。

应用程序通过字节传达信息。字节可以组成字符、原始数据、图形图像、数字语音、数字视频或者任何应用程序所需要的其他信息。系统I/O结构应该能持续可靠地将字节从设备传输到内存,反之亦然。这种传输在设备中往往包含一些机械运动,例如,磁盘的旋转,或键盘的敲击。数据传输所花费的时间远远大于处理器内部处理数据所需要的时间。所以I/O操作需要仔细计划和协调,以保证最优的性能。

C++中同时提供低层次的高层次的I/O功能。
低层次的I/O功能(也就是非格式化的I/O)指定字节应从设备传输到内存还是从内存传输到设备。这种传输通过针对单个字节,并提供速度快、容量大的传输。但是对于程序员来说不方便。
程序员通常更喜欢高层次的I/O(也就是格式化的I/O)。因为在这种输出方式中字节被组成了有意义的单元,例如整数、浮点数、字符、字符串或者用户自定义类型。除了大容量文件处理之外,这种面向类型的方法能够满足绝大多数的I/O处理。

1.传统流和标准流

C++的传统流库(<stdio.h>中的函数)允许输入/输出char类型的字符。一个char只占据一个字节的大小,它只能表示一个字符的有限集(例如,ASCII字符集)。然而,许多语言使用的字母表包含更多的符号,这是单字节的char无法表示的。

C++包含标准流库(,之类的库),可以描述这些新的字符。

2.iostream库的头文件

绝大多数C++程序都包含了<iostream>头文件,该头文件中声明了所有I/O操作所需的基础服务。其中定义了cin、cout、cerr和clog对象,分别对应于标准输入流、标准输出流、无缓冲的标准错误流和有缓冲的标准错误流。同时还提供了非格式化和格式化的I/O服务。

<iomanip>头文件中声明了参数化流操纵符(例如,之前我们使用过的setw和setprecision)用于向格式化I/O提供有用的服务。

<fstream>头文件中声明了文件处理服务。

3.输入/输出流的类的对象

iostream库提供了许多模板来处理一般的I/O操作。例如,类模板basic_istream支持输入流操作,类模板basic_ostream支持输出流操作,类模板basic_iostream同时支持输入流和输出流操作。


I/O流模板层次和运算符重载

模板basic_istream和basic_ostream派生自同一个基模板basic_ios。模板basic_iostream则从模板basic_istream和basic_ostream多重继承而来。下面是它们的UML类图。

在这里插入图片描述

运算符重载为输入和输出操作提供了更加方便的符号。左移运算符<<被重载用于实现流的输出,被称为流插入运算符,右移运算符>>被重载用于实现流的输入,被称为流提取运算符。这些运算符通常与标准流对象cin、cout、cerr和clog,以及用户自定义的流对象一起使用。


标准流对象cin、cout、cerr和clog

预定义对象cin是一个istream类的实例,并且被绑定到标准输入设备(通常是键盘)。在下面的语句中,流提取运算符>>用于使一个整形变量grade的值从cin输入到内存。
cin >> grade;
注意,由编译器确定grade的数据类型,并且选择合适的流提取运算符重载。假设grade已经被正确的声明,流提取运算符不需要附加任何类型信息(附加类型信息,就像C语言的I/O方式一样,在函数中显式的指定输入数据的类型)。标准库中重载的>>运算符可以用来输入内置类型、字符串和指针的值。

预定义对象cout是一个ostream类的实例,并且被绑定到标准输出设备(通常是显示器)。在下面的语句中,流插入运算符<<用于将变量grade的值从内存中输出到标准输出设备:
cout << grade;
注意,这里也是由编译器确定grade的数据类型,并且选择合适的流插入运算符,因此流插入运算符也同样不需要附加类型信息。

预定义对象cerrclog都是ostream类的实例,且都被绑定到标准错误设备。对象cerr的输出是无缓冲的。每个针对cerr的流插入的输出必须立刻显示,这对于迅速提示用户发生错误非常合适。而对象clog是输出是有缓冲的,每个针对clog的流插入的输出先保存到缓冲区中,直到缓冲区被填满或是被清空才会输出。


文件处理模板

C++文件处理乃至类模板basic_ifstreambasic_ofstreambasic_fstream。模板basic_ifstream继承自basic_istream,basic_ofstream继承自basic_ostream,而basic_fstream则继承自basic_iostream。它们的UML类图如下:

在这里插入图片描述

二、输出流

ostream提供了格式化的和非格式化的输出功能。输出功能包括使用流插入运算符<<执行标准数据类型的输出;通过成员函数put进行字符输出;通过成员函数write进行非格式化的输出;十进制、八进制、十六进制格式的整数输出;具有不同精确度的浮点数的输出,或是具有强制小数点的浮点数的输出;用指定符号填充数据域的输出;以及使用科学计数法和十六进制符号表示的大写字母的输出。

1.char* 变量的输出

C++能自动判定数据类型,这是它相对于C的一种改进。但是,这一特性有时候会产生一些问题。比如,现在我们要打印一个字符串的char*的值(该字符串首字符的地址)。然而,<<运算符已被重载用于打印将char*数据类型作为以空字符结尾的字符串。解决的办法就是将char*强制转化为void*类型(如果程序员想输出一个地址,那么就都应该对指针变量进行这样的转换)。

下面就是分别以字符串形式和地址形式输出char*的值的例子。

#include <iostream>
using namespace std;

int main()
{
	const char* const word = "hehe";

	cout << "Value of word is " << word
		<< "\nValue of static_cast<const void*>(word) is " 
		<< static_cast<const void*>(word) << endl;
}

运行结果:

在这里插入图片描述

2.使用成员函数put进行字符输出

可以使用成员函数put输出字符,例如,语句:
cout.put('A');
就可以显示单个字符A。put也可以级联使用(该成员函数返回的也是一个ostream对象的引用)。例如,
cout.put('A').put('B');

三、输入流

格式化的和非格式化的输入功能是由istream来提供的。流提取运算符通常跳过输入流中的空白字符(例如,空格、制表符和换行符),我们也可以改变它的这种行为。

在每个输入操作之后,流提取运算符给接收到所提取的信息的流对象返回一个引用。如果引用被用作判断条件(例如,while(cin >> grade)),那么将隐式调用流重载的void*强制转换运算符函数,根据最后输入操作的成功与否,将引用转化为非空指针或是空指针值。 非空指针值转化为bool值true,表示操作成功;空指针值则转化成bool值false,表示操作失败。

当试图超过流的末尾进行读取操作时,流重载的void*强制转化运算符返回一个空指针,表示已经读到文件的末尾。

每个流对象都包含一组状态位来控制流的状态(例如,格式化、设置错误状态等),流重载的void*强制类型转换运算符使用这些状态位来决定是返回非空值还是空值。当输入错误的数据类型时,流提取的failbit状态位被设置;当操作失败时,流的badbit位被设置。


1.get和getline成员函数

没有实参的成员函数get从指定流中输入一个字符(包括空白字符及其他非图形字符,比如表示文件尾的键序列等),并将这个值作为函数调用的返回值返回。这个版本的get函数在遇到流中的文件尾时返回EOF值。


使用成员函数eof、get和put

在这里插入图片描述
下面代码展示了成员函数eof、get和put的使用方法。

#include <iostream>
using namespace std;

int main()
{
	int character;

	cout << "Before input, cin.eof() is " << cin.eof() << endl
		<< "Enter a sentence followed by end-of-file:" << endl;

	while ((character = cin.get()) != EOF)
	{
		cout.put(character);
	}

	cout << "\nEOF in this system is: " << character << endl;
	cout << "After input of EOF, cin.eof() is " << cin.eof() << endl;
}

注意Windows系统中的文件结束符是通过组合键ctrl + z,UNIX系统中组合键ctrl + d

运行结果:

在这里插入图片描述
注意,只有当程序试图越过流中的最后一个字符进行读操作时,eof函数才返回true。也就是只有当文件指针指向结束符时,还要再将进行读取操作,此时eof函数才会返回true。

在这里插入图片描述

比较cin和cin.get

下面程序对使用流提取cin进行输入(读取字符直到遇到空白字符)和cin.get进行输入的不同之处。注意下面代码中使用get并没有指定分隔符,所以它使用默认的分隔字符(\n)。

#include <iostream>
using namespace std;

int main()
{
	const int SIZE = 80;
	char buffer1[SIZE];
	char buffer2[SIZE];

	cout << "Enter a sentence: " << endl;
	cin >> buffer1;

	cout << "\nThe string read with cin was:" << endl
		<< buffer1 << endl << endl;

	cin.get(buffer2, SIZE);

	cout << "The string read with cin.get was:" << endl
		<< buffer2 << endl << endl;
}

运行结果:

在这里插入图片描述
可以看到cin.get会遇到\n之前的,小于读取字符数的所有字符。而使用cin进行读取则只会读取到第一次遇到空白字符之前的字符。

成员函数getline

在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;

int main()
{
	string str1;
	string str2;

	cout << "Enter a sentence: " << endl;
	getline(cin, str1, ' ');

	cout << "\nThe string read with getline(cin, str1, ' ') was:" << endl
		<< str1 << endl << endl;

	getline(cin, str2);

	cout << "The string read with get(cin, str2) was:" << endl
		<< str2 << endl << endl;
}

运行结果:

在这里插入图片描述

可以看到使用getline获取的第二个string对象中,首字符并不是空格(被丢弃了)。

2.istream的成员函数peek、putback和ignore

在这里插入图片描述

在这里插入图片描述
putback这个成员函数对于扫描输入流,在其中搜索以特定字符开头的字段的应用程序很有用。

例如,此时我们要输出一个以字母’a’开头的单词,所以我们需要从流读取一个单词的首字符判断是否符合要求,在读取到一个单词的首字符为’a’时,我们要将这个单词保存到一个变量中,但是因为我们已经从流中读取了它的第一个字符,我们现在需要将这个字符’a’放回到流的最前面,使得我们可以从流中再次读取到这个完整的单词。

在这里插入图片描述

该函数也可以实现像上面的putback函数一样的预读操作。只是它压根没有把字符提取出来,所以也就不需要放回。

3.类型安全

C++提供类型安全的I/O。重载<<>>运算符可接收各种指定类型的数据项,如果遇到意料之外的数据类型,各种相应的错误位就会被设置,用户可以通过检测错误位来判断I/O操作是否成功。如果没有为用户自定义类型重载运算符<<和>>,并且试图输入或输出一个该用户自定义类型的对象的内容,那么编译器就会报错。

四、使用read、write和gcount的非格式化的I/O

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

下面给出一个使用read、write和gcount的例子。

#include <iostream>
using namespace std;

#define SIZE 80

int main()
{
	char buffer[SIZE];

	cout << "Enter a sentence:" << endl;
	cin.read(buffer, 20); // 从输入流提取20个字符存入buffer中

	cout << "The sentence entered was: " << endl;
	cout.write(buffer, cin.gcount()); // 之前提取了多少个字符,就输出多个字符
	cout << endl;
}

运行结果:

在这里插入图片描述

五、流操纵符简介

C++提供多种流操纵符来完成格式化的任务。流操纵符的功能包括设置域的宽度、设置精确度、设置和取消格式状态、设置域的填充字符、刷新流、向输出流添加新行(并刷新流)、在输出流中添加一个新字符、跳过输入流中的空白,等等。

1.整数流的基数:dec、oct、hex和setbase

为了能够更改流中整数的基数(默认的基数是10,也就是十进制),使之不局限于默认的基数,可以插入hex操纵符基数设置为十六进制,或者插入oct操纵符将基数设置为八进制。插入dec操纵符将整型流的基数重新设置为十进制。

当然除了使用上面提到的流操纵符,还可以使用setbase这个流操纵符来设置基数,该操纵符通过一个整数参数10、8、或16将基数分别设为十进制、八进制或十六进制。由于setbase有一个参数,所以也称为参数化流操纵符。而要使用任何参数化的操纵符就必须包含<iomanip>这个头文件。流的基数值只有被显式更改才会发生变化,所以上面提到这些流操纵符的设置都是“黏性”的。

下面给出这几个流操纵符的使用示例:

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
	int number;

	cout << "Enter a decimal number: ";
	cin >> number;

	cout << number << " in hexadecimal is " << hex << number << endl;
	cout << dec << number << " in octal is " << oct << number << endl;
	
	cout << setbase(10) << number << " in setbase(7) is " 
		<< setbase(7) << number << endl
		<< "in setbase(10) is " << setbase(10) << number << endl;
}

运行结果:

在这里插入图片描述

从结果中可以看出setbase流操纵符只能设置为10、8或16这三种基数。

2.浮点精度

可以使用流操纵符setprecisionios_base的成员函数precision来控制浮点数的精度(也就是小数点右边的位数)。这两种操作的设置都是黏性的。调用无参数的成员函数precision将返回当前的精度设置。

下面给出一个使用示例:

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
	double d = 3.14159265359;
	cout << "The default precision is " << cout.precision() << "\n\n";
	cout << "With default precision d is " << d << "\n";
	cout.precision(8);
	cout << "With high precision d is " << d << "\n\n";

	cout << "Current precision is " << cout.precision() << "\n\n";
	cout << setprecision(6) 
		<< "With setprecision(6) d is " << d << "\n\n";

	cout << fixed;

	cout << "\nAfter setting fixed:\n\n";
	cout << "With cout.precision(6) d is ";
	cout.precision(6);
	cout << d << "\n\n";
	cout << "With setprecision(8) d is " << setprecision(8)
		<< d << "\n\n";
}

运行结果:

在这里插入图片描述

从结果中看,如果没有使用流操纵符fixed,成员函数percision和参数化流操纵符setprecision设置的整个浮点数的有效数位;而使用fixed之后,它指定的就是小数点后面的有效位数。

3.位宽(width和setw)

成员函数width(基类是ios_basic)可以设置域宽(也就是输出值所占的字符位数或是可输入的最大字符数)并且返回原先的位宽。

在这里插入图片描述

如果输出值的宽度比域宽小,则插入填充字符进行填充。宽度大于指定宽度的值不会被截短,会将整个值都打印出来。

提示
宽度设置只适用于下一次输入或输出(非黏性),之后的宽度被隐式地设置为0(默认设置)。

使用示例:

#include <iostream>
using namespace std;

int main()
{
	int widthValue = 3;
	char sentence[10];

	cout << "Enter a sentence:" << endl;
	cin.width(5);  // 最多输入5个字符

	while (cin >> sentence)
	{
		cout.width(widthValue++);
		cout << sentence << endl;
		cin.width(5);
	}
}

运行结果:

在这里插入图片描述

注意,使用cin读取字符时,会在遇到空白字符时停下,并跳过空白字符,下一次读取直接从下一个非空白字符开始读取。
使用width成员函数,会读取(参数 - 1)个字符,因为需要在每次读取的字符串后面添加一个\0

所以才会出现上面的输出。

4.用户自定义输入流操纵符

程序员可以创建自己的流操纵符。下面的代码中展示了创建和使用新的无参数操纵符bellcarriageReturntabendLine。对于输出流操纵符来说,它们的返回类型都必须是ostream&类型的。

#include <iostream>
using namespace std;

// 输出一个\a
ostream& bell(ostream& output)
{
	return output << '\a';
}

// 输出一个 \r
ostream& carriageReturn(ostream& output)
{
	return output << '\r';
}

// 输出一个 \t
ostream& tab(ostream& output)
{
	return output << '\t';
}

// 输出一个 endl
ostream& endLine(ostream& output)
{
	return output << '\n' << flush;
}

int main()
{
	cout << "Testing the tab manipulator:" << endLine
		<< 'a' << tab << 'b' << tab << 'c' << endLine;

	cout << "Testing the carriageReturn and bell manipulator:" << endLine
		<< ".....................";
	cout << bell;

	cout << carriageReturn << "---------" << endLine;
}

运行结果:

在这里插入图片描述

六、流的格式状态和流操纵符

在I/O流操作中,可以使用多种流操纵符来指定各种格式。流操纵符可以控制输出格式。

下表中列出了类ios_base中可能会使用到的流操纵符。

流操纵符描述
skipws跳过输出流的空白字符。使用流操纵符noskipws来重置设置
left域的输出左对齐。必要时在右边填充字符
right域的输出右对齐。必要时在左边填充字符
internal表示域中的数字的符号左对齐,同时域的数字的数值部分右对齐(中间使用填充字符填充)
boolalpha指定bool类型的值以true或false的形式显示。noboolalpha指定bool类型的值以1或0的形式显示
dec整数以十进制数显示
oct整数以八进制数显示
hex整数以十六进制数显示
showbase指明在数字的前面显示该数的基数(以0开头表示八进制,0x表示十六进制)。使用流操纵符noshowbase可以取消数字前的基数显示
showpoint指明浮点数必须显示小数点。通常使用fixed流操纵符来确保小数点右边数字的位数,即使全部为0。可以使用流操纵符noshowpoint重置该设置
uppercase指明当显示十六进制数时使用大写字母,并且在科学计数法表示浮点数时使用大写字母E。可以使用流操纵符nouppercase来重置该设置
showpos在正数(负数本来就会显示符号)前显示加号(+)。可以使用流操纵符noshowpos来重置该设置
scientific以科学计数法显示输出浮点数
fixed以定点小数形式显示浮点数,并指定小数点右边的位数

1.尾数零和小数点(showpoint)

流操纵符showpoint强制要求浮点数的输出必须带小数点和尾数零。比如说浮点数79.0,在不使用showpoint时显示79,使用showpoint时则显示79.0000(尾数零取决于当前的精确度)。要重置showpoint的设定,需要使用流操纵符noshowpoint

当不使用fixedscientific时,精确度表示显示的有效位数,而不是小数点后的数字的位数。

#include <iostream>
using namespace std;

int main()
{
	cout << "Before using showpoint:" << '\n';
	cout << "6.6600 prints as: " << 6.6600 << endl
		<< "6.6000 prints as: " << 6.6000 << endl
		<< "6.0000 prints as: " << 6.0000 << "\n\n";

	cout << "After using showpoint:" << '\n';
	cout << showpoint << "6.6600 prints as: " << 6.6600 << endl
		<< "6.6000 prints as: " << 6.6000 << endl
		<< "6.0000 prints as: " << 6.0000 << "\n\n";
}

运行结果:

在这里插入图片描述

2.对齐(left、right和internal)

直接看下面的例子

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
	int x = 12345;

	cout << "Default is right justified:" << endl
		<< setw(10) << x;

	cout << "\n\nUse std::left to left justify x:\n"
		<< left << setw(10) << x;

	cout << "\n\nUse std::internal to internal justify x:\n"
		<< showpos << internal << setw(10) << x;

 	cout << "\n\nUse '*' to fill space x:\n"
		<< setfill('*') << setw(10) << x << endl;
 }

运行结果:

在这里插入图片描述

其中在使用internal流操纵符时,还辅以showpos和setfill这两个流操纵符,其中showpos是显示在数字前显示符号,setfill是设置在输出内容比域宽小时进行填充的字符。

3.内容填充(fill和setfill)

成员函数fill指定对齐域的填充字符。如果没有字符被指定,则默认使用空格符填充。fill函数返回设定之前的填充字符。setfill流操纵符也用于设置填充字符。

可以参照上一个示例代码。

其中成员函数fill的使用,只需要在其前面使用一个ostream对象即可,例如:cout.fill('*');

4.整数流的基数

使用流操纵符showbase可以要求整数的基数被输出。

例如:

#include <iostream>
using namespace std;
 
int main()
{
	int x = 100;

	cout << "Printing integers preceded by their base:" << endl
		<< showbase;

	cout << x << endl;
	cout << oct << x << endl;
	cout << hex << x << endl;
}

运行结果:

在这里插入图片描述

5.浮点数、科学计数法和定点小数记数法(scientific、fixed)

“黏性”流操纵符scientificfixed可以控制浮点数的输出格式。流操纵符scientific要求浮点数以科学计数法的格式输出。流操纵符fixed要求浮点数以指定小数位数的形式显示(可以使用成员函数precision或流操纵符setprecision指定小数位数)。如果不使用其他操纵符,由浮点数的值决定浮点数的输出格式。

使用示例:

#include <iostream>
using namespace std;

int main()
{
	double x = 0.001234567;
	double y = 1.946e9;

	cout << "Displayed in default format:\n"
		<< x << '\t' << y << endl;

	cout << "\nDisplayed in scientific format:\n"
		<< scientific << x << '\t' << y << endl;

	cout << "\nDisplayed in fixed format:\n"
		<< fixed << x << '\t' << y << endl;
}

运行结果:

在这里插入图片描述

6.大写/小写控制(uppercase)

流操纵符uppercase在输出十六进制整数基数和科学计数格式的浮点数时,分别输出大写字母X和E。还可以使十六进制整数中的字母都以大写字母形式显式。在默认情况下,十六进制和科学计数格式的浮点数中的字母都以小写字母显示。如果要取消uppercase的设置,只需要输出流操纵符nouppercase即可。

使用示例:

#include <iostream>
using namespace std;

int main()
{
	cout << "Printing uppercase letters in scientific" << endl
		<< "notation exponents and hexadecimal values:" << endl;

	cout << uppercase << 4.28e10 << endl
		<< hex << showbase << 123456789 << endl;
}

运行结果:

在这里插入图片描述

7.指定布尔类型(boolalpha)

boolalphanoboolalpha的设置为“黏性”的。直接看下面的示例:

#include <iostream>
using namespace std;

int main()
{
	bool booleanValue = true;

	cout << "booleanValue is " << booleanValue << endl;

	cout << "booleanValue (after using boolalpha) is "
		<< boolalpha << booleanValue << "\n\n";

	cout << "\nswitch booleanValue and use noboolalpha" << endl;
	booleanValue = false;
	cout << noboolalpha << endl;

	cout << "booleanValue is " << booleanValue << endl;
	cout << "booleanValue (after using boolalpha) is " 
		<< boolalpha << booleanValue << endl;
}

运行结果:

在这里插入图片描述

8.通过成员函数flags设置和重置格式状态

通过上面的学习,我们已经知道了如何使用流操纵符来更改输出格式,那么我们在使用了流操纵符设置了格式之后,要如何将输出格式重置为默认状态呢?
无参数的成员函数flags将当前的格式设置以fmtflags数据类型(ios_base类中的)的形式返回,它表示了格式状态。
拥有一个fmtflags参数的成员函数flags将格式状态设置为其参数指定的格式状态,并返回之前的状态设定。
在这里插入图片描述

使用示例:

#include <iostream>
using namespace std;

int main()
{
	int integerValue = 1000;
	double doubleValue = 0.0947628;

	// 显示当前的格式状态
	cout << "The value of the flags variable is: " << cout.flags()
		<< "\nPrint int and double in original format:\n"
		<< integerValue << '\t' << doubleValue << "\n\n";

	ios_base::fmtflags originalFormat = cout.flags();
	cout << showbase << oct << scientific; // 改变格式

	cout << "The value of the flags variable is: " << cout.flags()
		<< "\nPrint int and double in new format:\n"
		<< integerValue << '\t' << doubleValue << "\n\n";

	cout.flags(originalFormat); // 重置格式状态

	cout << "The restored value of the flags variable is: " << cout.flags()
		<< "\nPrint values in original format again:\n"
		<< integerValue << '\t' << doubleValue << "\n\n";
}

运行结果:

在这里插入图片描述

七、流的错误状态

流的状态可以通过检测ios_base类中的相应位来判断。
在这里插入图片描述
注意,在绝大多数情况下,如果eofbit被设置了,那么failbit也会被设置。

#include <iostream>
using namespace std;

int main()
{
	cout << "Before a bad input operation:"
		<< "\ncin.rdstate(): " << cin.rdstate()
		<< "\ncin.eof(): " << cin.eof()
		<< "\ncin.fail(): " << cin.fail()
		<< "\ncin.bad(): " << cin.bad()
		<< "\ncin.good(): " << cin.good() << "\n\n";

	int number;

	cout << "Expects an integer, but enter a character: ";
	cin >> number;

	cout << "\nAfter a bad input operation:"
		<< "\ncin.rdstate(): " << cin.rdstate()
		<< "\ncin.eof(): " << cin.eof()
		<< "\ncin.fail(): " << cin.fail()
		<< "\ncin.bad(): " << cin.bad()
		<< "\ncin.good(): " << cin.good() << "\n\n";

	cin.clear(); // 重置错误状态,该函数的默认参数是std::ios_base::goodbit

	cout << "After cin.clear()" 
		<< "\ncin.rdstate(): " << cin.rdstate()
		<< "\ncin.eof(): " << cin.eof()
		<< "\ncin.fail(): " << cin.fail()
		<< "\ncin.bad(): " << cin.bad()
		<< "\ncin.good(): " << cin.good() << "\n\n";
}

运行结果:

在这里插入图片描述

当遇到文件尾时,输入流有eofbit将被设置。当试图越过流的末尾提取数据时,程序可以调用成员函数eof来判断是否遇到了文件尾。遇到文件尾返回true,否则返回false

当在流中发生格式错误时,failbit位将被设置,并且不会读入任何字符,例如,像上面的测试程序一样,预期从流中接收一个整数,但是接收到一个字符,在遇到这种情况时,这些字符不会丢失。成员函数fail将报告流操作是否失败了。通常这种错误是可恢复的(数据没有丢失)。

当发生数据丢失错误时,将会设置badbit位。成员函数bad将报告流操作是否失败了。一般情况下,这种严重的错误是不可恢复的(数据已经丢失)。

如果流中的eofbit、failbit、badbit都没有被设置,那么goodbit将设置。

成员函数rdstate返回流的错误状态。可以使用该函数的返回值来判断错误状态,例如,将该函数的返回值与不同的错误位进行"相与",结果为1的即是出现错误的状态位。

成员函数clear重置流的错误状态,默认参数为goodbit,既将流的状态设为good
所以也可以使用下面的语句将流的状态设为错误cout.clear(ios::failbit);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2394016.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NW969NW978美光闪存颗粒NW980NW984

NW969NW978美光闪存颗粒NW980NW984 技术解析&#xff1a;NW969、NW978、NW980与NW984的架构创新 美光&#xff08;Micron&#xff09;的闪存颗粒系列&#xff0c;尤其是NW969、NW978、NW980和NW984&#xff0c;代表了存储技术的前沿突破。这些产品均采用第九代3D TLC&#xf…

使用 ssld 提取CMS 签名并重签名

拿SpringBoard的cms签名和entitlements.xml&#xff0c;对tihook.dylib进行重签名 工具来源&#xff1a;https://github.com/eksenior/ssld

大厂前端研发岗位PWA面试题及解析

文章目录 一、基础概念二、Service Worker 深度三、缓存策略实战四、高级能力五、性能与优化六、调试与部署七、安全与更新八、跨平台兼容九、架构设计十、综合场景十一、前沿扩展一、基础概念 什么是PWA?列举3个核心特性 解析:渐进式网页应用。核心特性:离线可用、类原生体…

第十四章 MQTT订阅

系列文章目录 系列文章目录 第一章 总体概述 第二章 在实体机上安装ubuntu 第三章 Windows远程连接ubuntu 第四章 使用Docker安装和运行EMQX 第五章 Docker卸载EMQX 第六章 EMQX客户端MQTTX Desktop的安装与使用 第七章 EMQX客户端MQTTX CLI的安装与使用 第八章 Wireshark工具…

腾讯云推出云开发AI Toolkit,国内首个面向智能编程的后端服务

5月28日&#xff0c;腾讯云开发 CloudBase 宣布推出 AI Toolkit&#xff08;CloudBase AI Toolkit&#xff09;&#xff0c;这是国内首个面向智能编程的后端服务&#xff0c;适配 Cursor 等主流 AI 编程工具。 云开发 AI Toolkit旨在解决 AI 辅助编程的“最后一公里”问题&…

前端-不对用户显示

这是steam的商店偏好设置界面&#xff0c;在没有被锁在国区的steam账号会有5个选项&#xff0c;而被锁在国区的账号只有3个选项&#xff0c;这里使用的技术手段仅仅在前端隐藏了这个其他两个按钮。 单击F12打开开发者模式 单击1处&#xff0c;找到这一行代码&#xff0c;可以看…

WPF【10_2】数据库与WPF实战-示例

客户预约关联示例图 MainWindow.xaml 代码 <Window x:Class"WPF_CMS.MainWindow" xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d"ht…

Cursor奇技淫巧篇(经常更新ing)

Dot files protection &#xff1a;Cursor当开启了Agent模式之后可以自动帮我们写文件&#xff0c;但是一般项目中的一些配置文件&#xff08;通常以.开头的&#xff09;都是非常重要性&#xff0c;为了防止Cursor在运行的过程中自己修改这些文件&#xff0c;导致风险&#xff…

Unity3D仿星露谷物语开发58之保存时钟信息到文件

1、目标 保存当前的时钟信息到文件中。 2、修改TimeManager对象 TimeManager对象添加组件&#xff1a;Generate GUID 3、修改SceneSave.cs脚本 添加1行代码&#xff1a; 4、修改TimeManager.cs脚本 添加&#xff1a; using System; 修改TimeManager类&#xff1a; 添加属…

lstm 长短期记忆 视频截图 kaggle示例

【官方双语】LSTM&#xff08;长短期记忆神经网络&#xff09;最简单清晰的解释来了&#xff01;_哔哩哔哩_bilibili . [short,input]*[2.7,1.63]b5.95 换参数和激活函数 tan激活函数输出带正负符号的百分比 tanx公式长这样&#xff1f; 潜在短期记忆 前几天都是乱预测&#xf…

Spring Advisor增强规则实现原理介绍

Spring Advisor增强规则实现原理介绍 一、什么是 Advisor&#xff1f;1. Advisor 的定义与本质接口定义&#xff1a; 2. Advisor 的核心作用统一封装切点与通知构建拦截器链的基础实现增强逻辑的灵活组合 二. Sprin当中的实现逻辑1 Advisor 接口定义2 PointcutAdvisor 接口定义…

【HarmonyOS 5】鸿蒙中的UIAbility详解(二)

【HarmonyOS 5】鸿蒙中的UIAbility详解&#xff08;二&#xff09; 一、前言 今天我们继续深入讲解UIAbility&#xff0c;根据下图可知&#xff0c;在鸿蒙中UIAbility继承于Ability&#xff0c;开发者无法直接继承Ability。只能使用其两个子类&#xff1a;UIAbility和Extensi…

力扣HOT100之动态规划:152. 乘积最大子数组

这道题并不是代码随想录里的&#xff0c;我试着用动规五部曲来做&#xff0c;然后不能通过全部测试样例&#xff0c;在第109个测试样例卡住了&#xff0c;如下所示。 原因是可能负数乘以负数会得到最大的乘积&#xff0c;不能单纯地用上一个序列的最大值乘以当前值来判断是否能…

定制开发开源AI智能名片S2B2C商城小程序:数字营销时代的话语权重构

摘要&#xff1a;在数据驱动的数字营销时代&#xff0c;企业营销话语权正从传统媒体向掌握用户数据与技术的平台转移。本文基于“数据即权力”的核心逻辑&#xff0c;分析定制开发开源AI智能名片S2B2C商城小程序如何通过技术赋能、场景重构与生态协同&#xff0c;帮助企业重构营…

【面试 - 遇到的问题 - 优化 - 地图】腾讯地图轨迹回放 - 回放的轨迹时间要和现实时间对应(非匀速)

目录 背景轨迹回放 - 匀速效果图TrackPlaybackDialog.vue 代码TMapNew.vue 代码 轨迹回放 - 非匀速效果图TrackPlaybackDialog.vue 代码TMapNew.vue 代码 背景 腾讯地图轨迹回放是匀速回放的&#xff0c;但是客户要求根据现实时间&#xff0c;什么时间点在某个点位 【腾讯地图轨…

利用计算机模拟和玉米壳废料开发新型抗病毒药物合成方法

参阅&#xff1a;Top 创新大奖 这个课题将农业废弃物资源化利用、计算机辅助药物设计和绿色化学完美结合&#xff0c;是一个极具创新性和应用前景的研究方向&#xff01; 以下是如何利用计算机模拟和玉米壳废料开发新型抗病毒药物合成方法的系统思路&#xff1a; 核心思路 玉…

【Docker】存储卷

【简介】 宿主机的某一目录与容器中的某一目录建立的一种绑定关系&#xff0c;这就是“存储卷” 它有三个特性 1.它可以绕过联合文件系统&#xff0c; 直接作用于宿主机的目录 2.容器和宿主机的这一绑定关系指向了同一目录&#xff0c; 因此两个目录之间的数据是同步的&#xf…

OpenCV图像认知(二)

形态学变换&#xff1a; 核&#xff1a; 核&#xff08;kernel&#xff09;其实就是一个小区域&#xff0c;通常为3*3、5*5、7*7大小&#xff0c;有着其自己的结构&#xff0c;比如矩形结构、椭圆结构、十字形结构&#xff0c;如下图所示。通过不同的结构可以对不同特征的图像…

t015-预报名管理系统设计与实现 【含源码!!!】

项目演示地址 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装预报名管理系统软件来发挥其高效地信息处理的…

LLM中的Loss与Logits详解

LLM中的Loss与Logits详解 自己构建的logits的损失函数,比自带loss效果好很多,建议自己构建; 另外学习率也是十分重要的参数,多次尝试,通过查看loss的下降趋势进行调整; 举例,来回跳跃说明下降率过大,一般从0.0001 开始尝试。 在深度学习中,logits 和 loss 是两个不…