一.概述
C++流是读写(输入和输出)逻辑的通用实现,让您能够用统一的模式读写数据。不管是磁盘或键盘读取数据,还是将输入写入显示器或磁盘,这些模式都相同。您只需使用合适的流类,类的实现将负责处理与设备和操作系统相关的细节。
如std:cout 是 ostream 类的一个对象,用于输出到控制台。要使用 std::cout,需要包含提供它的头文件,这个头文件还提供了 std::cin,让您能够从流中读取数据。
std::cout << "Hello World!" << std::endl;
流能够以一致的方式访问不同的设备,比如要将 Hello World 写入文本文件,可将同样的语法用于文件流对象 fsHello:
fsHello << "Hello World!" << endl;
用于写入流时,运算符<<被称为流插入运算符,可将其用于写入屏幕、文件等。
用于将流中的数据写入变量时,运算符>>被称为流提取运算符,可将其用于从键盘、文件等读取输入。
二. 重要的C++流类和流对象
C++提供了一组标准类和头文件,可帮助您执行重要而常见的输入/输出操作。
类/对象 | 用途 |
cout | 标准输出流,通常被重定向到控制台 |
cin | 标准输入流,通常用于将数据读入变量 |
cerr | 用于显示错误信息的标准输出流 |
fstream | 用于操作文件的输入和输出流,继承了 ofstream 和 ifstream |
ofstream | 用于操作文件的输出流类,即用于创建文件 |
ifstream | 用于操作文件的输入流类,即用于读取文 |
stringstream | 用于操作字符串的输入和输出流类,继承了 istringstream 和 ostringstream,通常用于在字符串和其 他类型之间进行转换 |
cout、cin 和 cerr 分别是流类 ostream、istream 和 ostream 的全局对象。由于是全局对象, 它们在 main( )开始之前就已初始化。
使用流类时,可指定为您执行特定操作的控制符(manipulator)。std::endl 就是一个这样的控制符, 您一直在使用它来插入换行符:
std::cout << "This lines ends here" << std::endl;
下表列出了其他几个控制符和标志。
类/对象 | 用途 |
输出控制符 | |
endl | 插入一个换行符 |
ends | 插入一个空字符 |
基数控制符 | |
dec | 让流以十进制方式解释输入或显示输出 |
hex | 让流以十六进制方式解释输入或显示输出 |
oct | 让流以八进制方式解释输入或显示输出 |
浮点数表示控制符 | |
fixed | 让流以定点表示法显示数据 |
scientific | 让流以科学表示法显示数据 |
<iomanip>控制符 | |
setprecision | 设置小数精度 |
setw | 设置字段宽度 |
setfill | 设置填充字符 |
setbase | 设置基数,与使用 dec、hex 或 oct 等效 |
setiosflag | 通过类型为 std::ios_base::fmtflags 的掩码输入参数设置标志 |
resetiosflag | 将 std::ios_base::fmtflags 参数指定的标志重置为默认值 |
三. 使用 std::cout 将指定格式的数据写入控制台
1.使用 std::cout 修改数字的显示格式
可以让 cout 以十六进制或八进制方式显示整数。
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
cout << "Enter an integer: ";
int input = 0;
cin >> input;
cout << "Integer in octal: " << oct << input << endl;
cout << "Integer in hexadecimal: " << hex << input << endl;
cout << "Integer in hex using base notation: ";
cout << setiosflags(ios_base::hex | ios_base::showbase | ios_base::uppercase);
cout << input << endl;
cout << "Integer after resetting I/O flags: ";
cout << resetiosflags(ios_base::hex | ios_base::showbase | ios_base::uppercase);
cout << input << endl;
return 0;
}
上述代码示例以修改 cout 显示用户输入的整数 input 的方式。使用了控制符 oct 和 hex。使用了 setiosflags( )让 cout 以十六进制方式(并使用大写字母)显示该数字,其结果是 cout 将 253 显示为 OXFD。使用了 resetiosflags( ),其效果是再次使用 cout 显示该整数时,将显示为十进制。要将显示整数时使用的基数改为十进制,也可使用下面这种方式:
cout << dec << input << endl;
对于诸如 Pi 等数字,可指定 cout 显示它们时使用的精度(小数点后面的位数), 还可指定以定点表示法或科学表示法显示它们。
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
const double Pi = (double)22.0 / 7;
cout << "Pi = " << Pi << endl;
cout << endl << "Setting precision to 7: " << endl;
cout << setprecision(7);
cout << "Pi = " << Pi << endl;
cout << fixed << "Fixed Pi = " << Pi << endl;
cout << scientific << "Scientific Pi = " << Pi << endl;
cout << endl << "Setting precision to 10: " << endl;
cout << setprecision(10);
cout << "Pi = " << Pi << endl;
cout << fixed << "Fixed Pi = " << Pi << endl;
cout << scientific << "Scientific Pi = " << Pi << endl;
cout << endl << "Enter a radius: ";
double radius = 0.0;
cin >> radius;
cout << "Area of circle: " << 2 * Pi*radius*radius << endl;
return 0;
}
2.使用 std::cout 对齐文本和设置字段宽度
可使用 setw( )控制符来设置字段宽度,插入到流中的内容将在指定宽度内右对齐。在这种情况下,还可使用 setfill( )指定使用什么字符来填充空白区域。
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
cout << "Hey - default!" << endl;
cout << setw(35);
cout << "Hey - right aligned!" << endl;
cout << setw(35) << setfill('*');
cout << "Hey - right aligned!" << endl;
cout << "Hey - back to default!" << endl;
return 0;
}
四. 使用 std::cin 进行输入
std::cin 用途广泛,能够将输入读取到基本类型(如 int、double 和 char*)变量中。还可使用 getline( )从键盘读取一行输入。
1.使用 std::cin 将输入读取到基本类型变量中
使用 cin 可将标准输入读取到 int、double 和 char 变量中。
#include<iostream>
using namespace std;
int main()
{
cout << "Enter an integer: ";
int inputNum = 0;
cin >> inputNum;
cout << "Enter the value of Pi: ";
double Pi = 0.0;
cin >> Pi;
cout << "Enter three characters separated by space: " << endl;
char char1 = '\0', char2 = '\0', char3 = '\0';
cin >> char1 >> char2 >> char3;
cout << "The recorded variable values are: " << endl;
cout << "inputNum: " << inputNum << endl;
cout << "Pi: " << Pi << endl;
cout << "The three characters: " << char1 << char2 << char3 << endl;
return 0;
}
2.使用 std::cin:get 将输入读取到 char*缓冲区中
cin 能够将输入直接写入 int 变量,也可将输入直接写入 char 数组(C 风格字符串):
cout << "Enter a line: " << endl;
char charBuf [10] = {0};
cin >> charBuf;
写入 C 风格字符串缓冲区时,务必不要超越缓冲区的边界,以免导致程序崩溃或带来安全隐患, 这至关重要。因此,将输入读取到 char 数组(C 风格字符串)时,下面是一种更好的方法:
cout << "Enter a line: " << endl;
char charBuf[10] = {0};
cin.get(charBuf, 9);
3.使用 std::cin 将输入读取到 std::string 中
#include<iostream>
#include<string>
using namespace std;
int main()
{
cout << "Enter your name: ";
string name;
cin >> name;
cout << "Hi " << name << endl;
return 0;
}
输入字符串中如果有空格,空格后的内容就会读取不到,要读取整行输入(包括空白),需要使用 getline( ):
#include<iostream>
#include<string>
using namespace std;
int main()
{
cout << "Enter your name: ";
string name;
getline(cin, name);
cout << "Hi " << name << endl;
return 0;
}
五. 使用 std::fstream 处理文件
C++提供了 std::fstream,旨在以独立于平台的方式访问文件。std::fstream 从 std::ofstream 那里继承了写入文件的功能,并从 std::ifstream 那里继承了读取文件的功能。
要使用 std::fstream 类或其基类,需要包含头文件<fstream>。
1.使用 open( )和 close( )打开和关闭文件
要使用 fstream、ofstream 或 ifstream 类,需要使用方法 open( )打开文件:
fstream myFile;
myFile.open("HelloFile.txt",ios_base::in|ios_base::out|ios_base::trunc);
if (myFile.is_open())
{
myFile.close();
}
open( )接受两个参数: 第一个是要打开的文件的路径和名称(如果没有提供路径,将假定为应用程序的当前目录设置); 第二个是文件的打开模式。在上述代码中,指定了模式 ios_base::trunc( 即便指定的文件存在,也重新创建它)、ios_base::in(可读取文件)和 ios_base::out(可写入文件)。
在上述代码中使用了 is_open( ),它检测 open( )是否成功。
保存到文件时,必须使用 close()关闭文件流。
还有另一种打开文件流的方式,那就是使用构造函数:
fstream myFile("HelloFile.txt",ios_base::in|ios_base::out|ios_base::trunc);
如果只想打开文件进行写入,可使用如下代码:
ofstream myFile("HelloFile.txt", ios_base::out);
如果只想打开文件进行读取,可使用如下代码:
ifstream myFile("HelloFile.txt", ios_base::in);
无论是使用构造函数还是成员方法open( )来打开文件流,都建议在使用文件流对象前,使用 open( )检查文件打开操作是否成功。
可在下述各种模式下打开文件流。
- ios_base::app:附加到现有文件末尾,而不是覆盖它。
- ios_base::ate:切换到文件末尾,但可在文件的任何地方写入数据。
- ios_base::trunc:导致现有文件被覆盖,这是默认设置。
- ios_base::binary:创建二进制文件(默认为文本文件)。
- ios_base::in:以只读方式打开文件。
- ios_base::out:以只写方式打开文件。
2.使用 open( )创建文本文件并使用运算符<<写入文本
#include<fstream>
#include<iostream>
using namespace std;
int main()
{
ofstream myFile;
myFile.open("HelloFile.txt", ios_base::out);
if (myFile.is_open())
{
cout << "File open successful" << endl;
myFile << "My first text file!" << endl;
myFile << "Hello file!";
cout << "Finished writing to file, will close now" << endl;
myFile.close();
}
return 0;
}
以 ios_base::out 模式(即只写模式)打开文件。检查 open( )是否成功,然后使用插入运算符<<写入该文件流。最后, 关闭文件流。
3.使用 open( )和运算符>>读取文本文件
#include<fstream>
#include<iostream>
#include<string>
using namespace std;
int main()
{
ifstream myFile;
myFile.open("HelloFile.txt", ios_base::in);
if (myFile.is_open())
{
cout << "File open successful. It contains: " << endl;
string fileContents;
while (myFile.good())
{
getline(myFile, fileContents);
cout << fileContents << endl;
}
cout << "Finished reading file, will close now" << endl;
myFile.close();
}
else
cout << "open() failed: check if file is in right folder" << endl;
return 0;
}
4.读写二进制文件
写入二进制文件的流程与前面介绍的流程差别不大,重要的是在打开文件时使用 ios_base::binary 标志。通常使用 ofstream::write 和 ifstream::read 来读写二进制文件。
#include<fstream>
#include<iomanip>
#include<string>
#include<iostream>
using namespace std;
struct Human
{
Human() {};
Human(const char* inName, int inAge, const char* inDOB) : age(inAge)
{
strcpy(name, inName);
strcpy(DOB, inDOB);
}
char name[30];
int age;
char DOB[20];
};
int main()
{
Human Input("Siddhartha Rao", 101, "May 1916");
ofstream fsOut("MyBinary.bin", ios_base::out | ios_base::binary);
if (fsOut.is_open())
{
cout << "Writing one object of Human to a binary file" << endl;
fsOut.write(reinterpret_cast<const char*>(&Input), sizeof(Input));
fsOut.close();
}
ifstream fsIn("MyBinary.bin", ios_base::in | ios_base::binary);
if (fsIn.is_open())
{
Human somePerson;
fsIn.read((char*)&somePerson, sizeof(somePerson));
cout << "Reading information from binary file: " << endl;
cout << "Name = " << somePerson.name << endl;
cout << "Age = " << somePerson.age << endl;
cout << "Date of Birth = " << somePerson.DOB << endl;
}
return 0;
}
首先创建了结构 Human 的一个实例(该结构包含属性 name、age 和 BOD), 并使用 ofstream 将其持久化到磁盘中的二进制文件 MyBinary.bin 中。接下来,使用另一个类型为 ifstream 的流对象读取这些信息。输出的 name 等属性是从二进制文件中读取的。该示例还演示了如何使用 ifstream::read 和 ofstream::write 来读写文件。使用了 reinterpret_cast,它让编译器将结构解释为 char*。read后使用 C 风格类型转换方式,这与write后的类型转换方式等价。
六. 使用 std::stringstream 对字符串进行转换
要使用 std::stringstream 类,需要包含头文件<sstream>。
#include<fstream>
#include<sstream>
#include<iostream>
using namespace std;
int main()
{
cout << "Enter an integer: ";
int input = 0;
cin >> input;
stringstream converterStream;
converterStream << input;
string inputAsStr;
converterStream >> inputAsStr;
cout << "Integer Input = " << input << endl;
cout << "String gained from integer = " << inputAsStr << endl;
stringstream anotherStream;
anotherStream << inputAsStr;
int Copy = 0;
anotherStream >> Copy;
cout << "Integer gained from string, Copy = " << Copy << endl;
return 0;
}
上方程序让用户输入一个整型值,并使用运算符<<将其插入到一个 stringstream 对象中; 然后, 使用提取运算符将这个整数转换为 string。接下来,将存储在 inputAsStr 中的字符串转换为整数,并将其存储到 Copy 中。
注意
- 只想读取文件时,务必使用 ifstream。
- 只想写入文件时,务必使用 ofstream。
- 插入文件流或从文件流中提取之前,务必使用 is_open( )核实是否成功地打开了它。
- 使用完文件流后,别忘了使用方法 close( )将其关闭。
- 使用代码 cin>>strData;从 cin 提取内容 到 string 中时,通常导致 strData 只包含空白前的文本,而不是整行。
- 函数 getline(cin, strData);从输入流中获取整行,其中包括空白。