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

【C++】cin 和 cout :标准输入输出流的深入解析 cin 和 cout 是学习 C++ 编程时最基础的流对象,其设计背后的自动类型识别和流操作思想,使得编程的学习过程更加平滑,降低了入门难度。在学习它们的过程中,我们不仅学习了如何编写代码,还学会了计算机如何进行输入输出处理。它们所展现的“字符流”处理模式让我们能够更高层次地理解程序与数据的交互。 通过对 cin 和 cout 的深入分析,我们发现编程不仅仅是逻辑推理与数值计算,更是人与机 2024.12.4-✍51min 阅读详情

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



一、流

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的数据类型,并且选择合适的流插入运算符,因此流插入运算符也同样不需要附加类型信息。

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


文件处理模板

C++文件处理乃至类模板basic_ifstream、basic_ofstream和basic_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.浮点精度

可以使用流操纵符setprecision或ios_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.用户自定义输入流操纵符

程序员可以创建自己的流操纵符。下面的代码中展示了创建和使用新的无参数操纵符bell、carriageReturn、tab和endLine。对于输出流操纵符来说,它们的返回类型都必须是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。

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

#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)

“黏性”流操纵符scientific和fixed可以控制浮点数的输出格式。流操纵符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)

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

#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);

C++入门到精通学习路线 3. 就业前景:C++开发人员的就业前景良好。随着科技的发展和行业的需求增加,对于具备C++编程技能的人才的需求也在不断增加。- 《Effective C++》:这是一本经典的C++书籍,详细介绍了C++的最佳实践和常见陷阱,对于提高C++编程技能非常有帮助。- C++标准和编程规范:学习C++的最新标准和编程规范,如C++11、C++14和C++17等,了解C++的新特性和最佳实践。- 《C++ Primer Plus》:这是一本非常全面的C++教材,涵盖了C++的各个方面,适合进阶和精通阶段的学习。 阅读详情

相关推荐

C/C++基础到高级入门到精通的学习…

C++ 相比其他语言要复杂很多,我们平时做做需求也不会用到太多特性。新手学习C++的学习是肯定痛苦的,其实每个人开始都是真心想去学 习,但是苦于没有一点方向去指引。C/C++遍布的领域有后端服务器,移动互联网后端,经典Windows界面开发,移动互联网端界面开发,跨平台界面开发,图形分析,系统架构,应用设计,还有现在最为流行的分布架构等一些新的技术领域。所以说:C/C++俨然已成为一门贵族语言,成

qq3045528107的博客 1267

C/C++从入门到精通

C/C++从入门到精通 点击此处下载word版  第一阶段-零基础入门   C语言的作用、学习方法 1. C语言的作用 物联网开发 嵌入式开发 Linux内核开发 Linux系统开发 Linux驱动开发 研究算法、数据结构的必备语言。 学习C++的入门语言。 2. C语言的学习方法 大道至简 1. 不要刻意记忆语法规则。 2. 以项目为导向,在解决项目问题中学习。 ...

聆回的博客 2万+

Windows下Fortran调用CGNS库的完整踩坑实录:从HDF5、CMake到VS2019+IVF2020的保姆级配置

本文详细记录了在Windows环境下使用Fortran调用CGNS库的完整配置过程,涵盖HDF5安装、CMake配置、VS2019与IVF2020集成等关键步骤。通过实战经验分享,帮助开发者解决常见编译错误和链接问题,实现高效的Fortran与CGNS库集成。

weixin_28707739的博客 349

C/C++从入门到精通(入门、进阶、精通)

今天在网上偶然看见许多大师在讨论如何学习C/C++,大牛们推荐了一些书籍,个人觉得很不错,怕以后想学的时候找起来费劲,所以给整过来了。(本文转自大牛、大师们) 我的个人的拙见可以这样学C++: 第一步:首先入门可以从 国内出版的 教材入门 如  潭浩强的>  清华大学出版的>等 能了解一些基本语法 会编写些简单的程序  第二步:看>我绝对对赞同这是本好书,面也广.>这本书的名头也

kentyu001的博客 4万+

Arduino-CRSF库详解:嵌入式端CRSF协议解析与ELRS接收机集成

CRSF(Crossfire Serial Protocol)是一种面向FPV无人机的轻量级、高实时性串行通信协议,采用单总线异步传输与8位CRC-8校验机制,广泛应用于遥控链路与遥测交互。其核心在于帧同步(0xC8起始字节)、反相逻辑电平及无重传的广播式设计,对嵌入式平台的UART精度、电平匹配和状态机健壮性提出严苛要求。Arduino-CRSF库正是为资源受限MCU(如ESP32、STM32、ATmega328P)提供零配置、中断安全的协议解析能力,封装了CRC查表计算、超时恢复、多级同步验证等关键机制

weixin_35756690的博客 415

windows10下Boost的安装与初试

https://blog.youkuaiyun.com/s_lisheng/article/details/72871218 https://blog.youkuaiyun.com/yockie/article/details/8856190?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-12.vipsorttest&depth_1-utm_source=dis

焦露的博客 6394

C++ 输入输出流示例代码剖析

本文深入解析C++中自定义类型与输入输出流的交互实现,重点探讨了类型转换操作符、输入输出操作符重载技术,以及文件二进制和文本读写方式。通过A类、Date类的类型转换和流操作重载示例,展示了如何增强自定义类型的兼容性;在文件操作部分,详细比较了二进制读写的高效性与文本读写的可读性差异,并以ServerInfo结构体和ConfigManager类为例说明实际应用场景。这些技术广泛应用于网络配置、日志记录等开发需求,是C++数据处理能力的核心体现。

nplplus的博客 1170

Python实战:用pdfplumber从PDF中精准提取表格数据(附完整代码)

本文详细介绍了如何使用Python库pdfplumber从PDF中精准提取表格数据,包括环境配置、标准与复杂表格处理策略、数据清洗与验证等实战技巧。通过完整代码示例,帮助数据分析师和财务专业人士实现PDF数据提取的自动化,提升工作效率。

weixin_29061821的博客 398

C++11深度剖析

深度剖析了c++11的新特性和底层原理,详细讲解了各类特性的用法

You build it, you run it 6046

瑞芯微RK3568开发板实战:Android11系统parameter.txt文件配置全解析(附常见问题排查)

本文深入解析了瑞芯微RK3568开发板在Android 11系统下parameter.txt文件的配置要点与实战技巧。作为BootLoader启动阶段的核心蓝图,该文件决定了硬件初始化、分区布局及电源管理。文章详细拆解了身份标识、GPIO控制(PWR_HLD)、命令行参数及分区表等关键字段的配置逻辑,并针对RK3568平台与Android 11特性提供了专项适配策略,最后附带了启动失败、分区异常等常见问题的深度排查方法。

php55的博客 895

ScanNet数据集详解

ScanNet 是一个 RGB-D 视频数据集,在超过 1500 次扫描中包含了 250 万个视图,并标注了 3D 相机位姿、表面重建和实例级的语义分割。为了收集这些数据,设计了一个易于使用且具有扩展性的 RGB-D 捕获系统,包括自动化的表面重建和众包的语义标注。如果您想下载 ScanNet 数据,请使用您的机构邮箱地址填写一份 ScanNet 使用条款协议,并发送至 scannet@googlegroups.com。压缩包是将3D的标注信息“拍扁”成2D图片的结果,对应原始视频的每一帧。

frostmelody 全网同名,大家多多关注呀~ 持续分享优质内容! 3918

C++11 新增语法剖析

带你一命通关 C++11 新增语法 十分钟通透 C++11

qq_61500888的博客 3406

YOLO26中的PPM空间金字塔池化:改良设计与多尺度特征提取教程

在计算机视觉的语义分割任务中,准确理解图像中的上下文信息至关重要。例如,在街景图像中,可能同时存在远处的小汽车和近处的大建筑物,网络需要具备捕获多尺度上下文信息的能力。例如,要正确识别图像中的"船",不仅需要看到船本身的特征,还需要理解周围的水面环境。金字塔的每一层对应不同的池化尺度,从细粒度到粗粒度,形成了多层次的特征表示。PPM(Pyramid Pooling Module)正是为了解决这一问题而提出的模块,它通过构建金字塔形状的池化结构,有效地聚合不同尺度的上下文信息,从而提升语义分割的精度。

LXY_LOVE36的博客 82

13 输入/输出流的深入剖析(cpp大学教程)学习笔记

13.2 流 非格式化与格式化I/O 非格式化I/O(低层次I/O):通过字节的方式从设备传输到内存传输,提供速度快、容量大的传输 格式化I/O(高层次I/O):通过字节组成特定的字符串、整数、浮点数和用户自定义类型传输,可能会导致性能的问题,传输大容量的文件速度较慢,并且可移植性也存在问题 传统流和标准流 传统流:传统流库允许输入/输出char类型的字符,一个char类型的字符占一个字节 标准流:标准流库允许输入/输出wchar_t类型的Unicode字符,一个wchar_t类型的字符占两个字

stay_alive001的博客 963

kaggle学习笔记-餐厅数据挖掘

分析Zomato数据集的基本思想是为了公平地了解影响在班加罗尔不同地方建立不同类型餐厅的因素,每个餐厅的总评级,班加罗尔是这样一个城市,拥有超过12,000家餐厅,餐厅供应来自世界各地的菜肴。每天都有新的餐馆开业,这个行业还没有饱和,需求也在与日俱增。然而,尽管需求不断增加,新餐馆却很难与老牌餐馆竞争。大多数餐厅都供应同样的食物。班加罗尔是印度的IT之都。这里的大多数人主要依靠餐馆的食物,因为他们没有时间自己做饭。由于对餐馆的需求如此巨大,因此研究一个地方的人口统计学就变得很重要。

优快云XXCQ的博客 1862

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

盘点适合入门学习的C/C++开源项目

C 项目名称: C 项目简介: 是的,你没有看错,这个项目的名字就是单个字母C。C是一个宝藏项目,可以说是学习数据结构和刷算法题的利器,因为里面包含了几乎各种基础算法、数据结构、以及LeetCode算法题的C语言实现。具体包括: 客户端/服务器问题 统计方法问题 进制转换问题 各种数据结构:数组、链表、字典、二叉树、堆、栈、队列、哈希、图等等 搜索/查找问题 排序问题 LeetCode习题 其他杂项问题 注意,下图中只是截取了一部分数据结构和算法题的具体实现: 对于我们的作用: 可以帮助我们更好的学习数据

OceanStar的博客 3万+

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

C++从入门到起飞之——输入&输出 全方位剖析!

• 定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中 即为命名空间的成员。命名空间中可以定义变量/函数/类型等。//变量int rand;//函数return;//类,后面会讲到class testpublic:return;int b;• namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量,所以下 ⾯的rand不在冲突了。• C++中域有函数。

2301_80221228的博客 1876

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

【C++】输入输出流(IO流)

本文全面剖析C++的输入输出流(IO流)机制,对比C语言的scanf与printf,详解C++流的概念及其核心类(如cin、cout、文件流和字符串流)。内容涵盖标准IO流的特性、缓冲机制、错误状态处理,以及如何通过sync_with_stdio和tie优化IO性能。此外,深入探讨文件流的文本与二进制读写、自定义类型运算符重载实现序列化,并结合实例展示stringstream在结构化数据转换中的应用。文章还针对竞赛编程和实际开发场景,提供输入输出处理的实用技巧与注意事项,助力开发者高效掌握C++流的核心技术

2303_77756141的博客 1250

超详细 C/C++ 学习路线分析

C/C++遍布的领域有后端服务器,移动互联网后端,经典Windows界面开发,移动互联网端界面开发,跨平台界面开发,图形分析,系统架构,应用设计,分布架构,还有现在最为流行的人工智能等一些新的技术领域。所以说:C/C++俨然已成为一门贵族语言,成为了编程界最重要的语言之一。 C/C++在编程语言中的重要性不言而喻,那么,我们如果想要学习C/C++,应该怎么去学习?需要学习一些什么东西呢? 一、C/C++入门阶段 刚入门的时候我们主要是学习培养编程思维和动手能力,深刻理解面向过程和面向对象的思想方法,这对以后

C/C++Linux、音视频、DPDK 6210

C++之旅-C++11的深度剖析(1)

随着现代软件开发的快速发展,编程语言也在不断进化,C++ 作为一种功能强大的编程语言,已经经历了多个版本的更新,每一次版本的发布都为开发者带来了新的特性和功能。C++11是 C++ 语言的一个重要版本,它于 2011 年正式发布,并引入了许多全新的特性,极大地提升了代码的效率、可读性以及程序的执行性能。C++11 的发布不仅对 C++ 开发者来说是一次重大提升,它还为整个编程社区提供了更加现代化的编程工具。

2302_79376097的博客 3869

C++从入门到精通书籍大全推荐

在这本书中,作者全面论述了C++ 的历史和发展,C++中各种重要机制的本质、意义和设计背景,这些机制的基本用途和使用方法,讨论了C++ 所适合的应用领域和未来发展前景。5、 C++沉思录这本书总共分为6篇,共32章,分别对C++语言的历史和特点、类和继承、STL与泛型编程、库的设计等几大技术话题进行了详细而深入的讨论,细微之处几乎涵盖了C++所有的设计思想和技术细节。这本书我看了好多遍,前两遍看的是电子版,看完不过瘾,在图书馆借了实体书出来,看了一遍,然后又从京东买了一本。可惜的是,本书已经停版。

二进制君 3096

C++输入输出流使用中的易错点解析

摘要:C++输入输出流在使用过程中存在多个易错点,主要包括标准输入输出流(iostream)的类型匹配问题、混合使用cin和getline时的换行符陷阱,以及输出格式控制的常见问题。文件流(fstream)则需注意文件打开失败检查、资源管理和状态处理。字符串流(sstream)需警惕重复使用和类型转换错误。通过合理使用错误检查、缓冲区清理和格式控制工具,可显著提高代码健壮性。建议始终验证输入有效性,及时处理流状态,并使用<iomanip>进行精确输出控制,避免常见陷阱。(149字)

qq_39980997的博客 1453

C++学习-入门到精通【15】异常处理深入剖析

下面代码中我们定义类,该类是标准库(在头文件中定义)的一个派生类。类是标准库exception类(在头文件)的派生类,它是C++用于描述运行时错误所创建的标准基类。而类exception是C++标准库中描述所有异常而建立的标准基类。一个从类派生出来的典型异常类只定义了一个构造函数,这个构造函数将带有错误信息的字符串传递给基类runtime_error的构造函数。所有的异常类都直接或间接地从含有虚函数what的exception类派生而来,而what函数返回一个异常对象的错误信息。

PLKDTHFS的博客 1132

C/C++学习路线 (系统的学习路线

C/C++学习路线 随着互联网及互联网+深入蓬勃的发展,经过40余年的时间洗礼,C/C++俨然已成为一门贵族语言,出色的性能使之成为高级语言中的性能王者。而在今天,它又扮演着什么样重要的角色呢?请往下看: 后端服务器,移动互联网后端,经典Windows界面开发,移动互联网端界面开发,跨平台界面开发,图形分析,系统架构,应用设计,还有现在最为流行的分布架构等一些新的技术领域。 这么牛的语言,...

我从小酷到大的 2250
上一篇: C++学习-入门到精通【10】面向对象编程:多态性
下一篇: C++学习-入门到精通【12】文件处理
YKPG
博客等级 码龄6年 344粉丝 37原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值