C++ —— 使用流
Posted 我受到了惊吓
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ —— 使用流相关的知识,希望对你有一定的参考价值。
内容总结于书《C++高级编程》第四版
作为I/O机制,C语言中的printf()和scanf()确实很灵活,可以读取特定格式的数据,或输出格式化代码允许的任何值。然而这些函数不能很好的处理错误,并且在C++面向对象的语言中,它们根本不是面向对象的。
C++通过一种称为流(stream)的机制提供了更精良的输入输出方法。流是一种灵活且面向对象的I/O方法。
1 流的含义
所有的流都可以看成数据滑槽,流的方向不同,关联的目的地和来源也不同。例如,cout流是一个输出流,因此他的方向是流出;这个流将数据写入控制台,因此它关联的目的地是“控制台”。下表描述了所有预定义的流。
流 |
说明 |
cin |
An input stream, reads data from the “input console. |
cout |
A buffered output stream, writes data to the “output console. |
cerr |
An unbuffered output stream, writes data to the “error console,” which is often the same as the “output console. |
clog |
A buffered version of cerr. |
缓冲的流和非缓冲的流的区别在于,前者不是立即将数据发送到目的地,而是将缓冲输入的数据,然后以块的方式发送;而非缓冲的数据则立即发送,缓冲的目的通常是提高性能,对于某些目的地(如文件)而言一次性写入较大的块时速度更快。可以使用flush()方法刷新缓冲区,强制要求缓冲的流将其当前所有的缓冲数据发送到目的地。
2 流式输出
2.1 输出的基本概念
输出流定义在<ostream>头文件中。我们经常会在程序中包含<iostream>头文件,这个头文件又包含输入流和输出流头文件。<iostream>头文件还声明了所有预定义的流实例:cout、cin、cerr、clog以及对应的宽版本。
使用输出流最简单的方法是使用<<运算符。通过<<可输出C++的基本类型,如int、指针、double和字符。C++的string类也兼容<<,C风格的字符串也能正确输出。下面列举使用<<的示例:
int i = 7;
cout << i << endl;
char ch = 'a';
cout << ch << endl;
string myString = "Hello World.";
cout << myString << endl;
输出结果:
7
a
Hello World.
cout是写入控制台的内建流,控制台也称标准输出(standard output)。可将<<的使用串联起来,从而输出多个数据段。这是因为<<运算符返回一个流的引用,因此可以立即对同一个流再次应用<<运算符。
C++流可以正确解析C风格的转义字符,例如包含\n的字符串,也可以使用std::endl开始一个新行。\n和endl的区别是,\n仅开始新的一行,而endl还会刷新缓存区。使用endl时要小心,因为过多的缓存区刷新会降低性能。
2.2 输出流的方法
put()和write()
put()接受单个字符,write()接受一个字符数组。传给这些方法的数据按照原本的形式输出,没有做任何特殊的格式化和处理操作。下面一段代码接受一个C风格的字符串,并将它输出到控制台,这个函数没有使用<<运算符:
const char* test = "hello there\n";
cout.write(test, strlen(test));
下面的代码段通过put()方法,将C风格字符串的给定索引输出到控制台:
cout.put('a');
flush()
在以下任意一种条件下,流将刷新积累的数据
遇到sentine(如endl标记)时。
流离开作用域被析构时。
要求从对应的输入流输入数据时。
流缓存满时。
显示的要求流刷新缓存时。
显示要求流刷新缓存的方法是调用flush方法:
cout << "abc";
cout.flush(); // abc is written to the console.
cout << "def";
cout << endl; // def is written to the console.
2.3 输出操作算子
流的一项独特特性是,放入滑槽的内容并非仅限于数据。C++流还能识别操作算子,操作算子是能修改流行为的对象,而不是流能够操作的数据。
endl就是一个操作算子。endl操作算子封装了数据和行为。它要求流输出一个行结束序列,并且刷新缓存。下面列出了其他有用的操作算子,大部分定义在<ios>和<iomanip>标准头文件中。
boolalpha和noboolalpha:要求流将布尔值输出为true和false或1和0。默认是noboolalpha。
hex、oct、和dec:分别以十六进制、八进制和十进制输出数据。
setprecision:设置输出小数时的小数位数。
setw:设置输出数值数据的宽度
setfill:当数字宽度小于指定宽度时,设置用于填充的字符。
showpoint和noshowpoint:对于不带小数部分的浮点数,强制流总是显示或者不显示小数点。
put_money:向流写入一个格式化的货币值。
put_time:向流写入一个格式化的时间值。
quoted:把给定的字符串封装在引号中,并转义嵌入的引号。
// Boolean values
bool myBool = true;
cout << "This is the default: " << myBool << endl;
cout << "This should be true: " << boolalpha << myBool << endl;
cout << "This should be 1: " << noboolalpha << myBool << endl;
// Simulate "%6d" with streams
int i = 123;
printf("This should be ' 123': %6d\n", i);
cout << "This should be ' 123': " << setw(6) << i << endl;
// Simulate "%06d" with streams
printf("This should be '000123': %06d\n", i)
cout << "This should be '000123': " << setfill('0') << setw(6)<< i << endl;
// Fill with *
cout << "This should be '***123': " << setfill('*') << setw(6)<< i << endl;
// Reset fill character
cout << setfill(' ');
// Floating point values
double dbl = 1.452;
double dbl2 = 5;
cout << "This should be ' 5': " << setw(2) << noshowpoint <<dbl2 << endl;
cout << "This should be @@1.452: " << setw(7) << setfill('@') <<dbl << endl;
// Reset fill character
cout << setfill(' ');
// Instructs cout to start formatting numbers according to yourlocation.
cout.imbue(locale(""))
// Format numbers according to your location
cout << "This is 1234567 formatted according to your location: "<< 1234567<< endl;
// Monetary value. What exactly a monetary value means dependson your
//location. For example, in the USA, a monetary value of 120000means 120000
// dollar cents, which is 1200.00 dollars.
cout << "This should be a monetary value of 120000, "<< "formatted according to your location: "<< put_money("120000") << endl;
// Date and time
time_t t_t = time(nullptr);
// Get current system time
tm* t = localtime(&t_t);
// Convert to local time
cout << "This should be the current date and time "<< "formatted according to your location: "<< put_time(t, "%c") << endl;
// Quoted string
cout << "This should be: \"Quoted string with \\\"embeddedquotes\\\".\": "<< quoted("Quoted string with \"embedded quotes\".") <<endl;
3 流式输入
3.1 输入的基本概念
通过输入流可以采取两种办法来读取数据。第一种方法是使用运算符>>。通过>>从输入流读入数据时,代码提供变量保存接受的值。如下:
string userInput;
cin >> userInput;
cout << "User input was " << userInput << endl;
默认情况下,>>运算符根据空白字符对输入值进行标志化。例如,如果用户运行以上程序,并键入hello world作为输入,那么只有第一个空白字符之前的字符才会存储在userInput变量中。输出如下所示:
User input was hello
在输入中包含字符的一种方法是使用get()。
通过输入流可以读入多个值,并且可根据需要混合和匹配类型。例如:
string s;
int a;
cin>>s>>a;
3.2 处理输入错误
输入流提供了一些方法用于检测异常情况。大部分和输入流有关的错误条件都发生在无数据可读时。例如,可能到达流尾。查询输入流状态的最常见的方法是在条件语句中访问输入流。例如,只要cin保持在“良好”状态,下面的循环就会继续进行:
while (cin) { ... }
同时可以输入数据:
while (cin ch) { ... }
还可以调用good()、bad()和fail()方法。还有一个eof()方法,如果到达文件尾部,就返回true。与输出流类似,遇到文件结束标记时,good()和fail()都会返回false。
下面展示了从流中读取数据并处理错误的常用模式。
cout << "Enter numbers on separate lines to add. "<< "Use Control+D to finish (Control+Z in Windows)." <<endl;
int sum = 0;
if (!cin.good()) {
cerr << "Standard input is in a bad state!" << endl;
return 1;
}
int number;
while (!cin.bad()) {
cin >> number;
if (cin.good()) {sum += number;}
else if (cin.eof()) {
break; // Reached end of file
}
else if (cin.fail()) {// Failure!
cin.clear(); // Clear the failure state.
string badToken;
cin >> badToken; // Consume the bad input.
cerr << "WARNING: Bad input encountered: " << badToken<< endl;
}
}
cout << "The sum is " << sum << endl;
3.3 输入方法
get()
get()最简单的版本返回流中的下一个字符,其他版本一次读如多个字符。get通常避免>>运算的自动标志化。例如,下面这个函数从输入流中读如一个由多个单词构成的名字,一直读到流尾。
string readName(istream& stream){
string name;
while (stream) { // Or: while (!stream.fail())
int next = stream.get();
if (!stream || next == std::char_traits<char>::eof())
break;
name += static_cast<char>(next);// Append character.
}
return name;
}
get的返回值保存在int而不是char变量中,因为get()会返回一些特殊的非字符值,例如std::char_traits<char>::eof(),因此使用int。
unget()
对于大多数场合来说,理解输入流的正确方式时将输入流理解为单向的滑槽。数据被丢入滑槽,然后进入变量。unget()打破了这个模型,允许将数据塞回滑槽。调用unget()会导致流退回一个位置,将读如的前一个字符放回流中。
peek()
peek()方法可以预览调用get()后返回的下一个值,可以想象为查看一下滑槽,但不把值取出来。
getline()
从输入流中获取一行数据是一种常见的需求。getline()方法用一行数据填充字符缓冲区。指定的大小包括\0字符。因此,以下代码从cin读取最多kBufferSize-1个字符,或者直到读取行尾序列为止:
char buffer[kBufferSize] = { 0 };
cin.getline(buffer, kBufferSize);
调用getline()时,从输入流中读取一行,读到行尾位置。不过行尾不会出现在字符串中。行尾序列和平台相关,可以是\n\r、\r或\r\n。
有个版本的get()函数执行的操作和getline()一样,区别在于get()把换行序列留在输入流中。
4 对象的输入输出
即使不是基本类型,也可以通过重载<<和>>运算符的方式输入输出对象。下面给出重载示例:
class SpreadsheetCell{
// Omitted for brevity
};
std::ostream& operator<<(std::ostream& ostr, constSpreadsheetCell& cell);
std::istream& operator>>(std::istream& istr, SpreadsheetCell&cell);
ostream& operator<<(ostream& ostr, const SpreadsheetCell& cell){
ostr << cell.getValue();
return ostr;
}
istream& operator>>(istream& istr, SpreadsheetCell& cell){
double value;
istr >> value;
cell.set(value);
return istr;
}
以上是关于C++ —— 使用流的主要内容,如果未能解决你的问题,请参考以下文章