跳到主要内容

C++ 流操纵算子

在C++编程中,流操纵算子(Stream Manipulators)是一组特殊的辅助函数,用于控制和格式化输入输出流。它们可以改变数据的表示方式,设置精度,指定数字进制,以及控制其他格式化选项。掌握流操纵算子的使用将帮助你创建更专业、易读的输出。

什么是流操纵算子?

流操纵算子是一种函数或对象,可以通过插入(<<)或提取(>>)运算符与流一起使用,从而修改流的状态或影响数据的格式化方式。

备注

流操纵算子本质上是一种特殊的函数,它们对输入输出流的行为进行修改,而不是处理具体的数据。

常用的流操纵算子

格式化标志操纵算子

这些操纵算子用于设置或清除格式化标志,以控制输出的格式。

设置数值的进制

cpp
#include <iostream>
#include <iomanip>

int main() {
int num = 255;

std::cout << "十进制: " << std::dec << num << std::endl;
std::cout << "十六进制: " << std::hex << num << std::endl;
std::cout << "八进制: " << std::oct << num << std::endl;

return 0;
}

输出结果:

十进制: 255
十六进制: ff
八进制: 377

对齐和填充

cpp
#include <iostream>
#include <iomanip>

int main() {
int num = 42;

// 设置字段宽度为10,默认右对齐
std::cout << "右对齐: |" << std::setw(10) << num << "|" << std::endl;

// 左对齐
std::cout << "左对齐: |" << std::left << std::setw(10) << num << "|" << std::endl;

// 使用*填充剩余空间
std::cout << "填充字符: |" << std::right << std::setfill('*') << std::setw(10) << num << "|" << std::endl;

return 0;
}

输出结果:

右对齐: |        42|
左对齐: |42 |
填充字符: |********42|

精度控制操纵算子

这些操纵算子用于控制浮点数的显示精度。

cpp
#include <iostream>
#include <iomanip>

int main() {
double value = 3.14159265358979;

// 设置精度为4位有效数字
std::cout << "默认精度: " << value << std::endl;
std::cout << "4位精度: " << std::setprecision(4) << value << std::endl;

// 固定小数点格式,显示2位小数
std::cout << "固定小数点(2位): " << std::fixed << std::setprecision(2) << value << std::endl;

// 科学计数法格式,显示3位小数
std::cout << "科学计数法(3位): " << std::scientific << std::setprecision(3) << value << std::endl;

return 0;
}

输出结果:

默认精度: 3.14159
4位精度: 3.142
固定小数点(2位): 3.14
科学计数法(3位): 3.142e+00

布尔值格式化操纵算子

这些操纵算子用于控制布尔值的显示方式。

cpp
#include <iostream>
#include <iomanip>

int main() {
bool flag1 = true;
bool flag2 = false;

// 默认情况下,布尔值显示为1或0
std::cout << "默认布尔值: " << flag1 << " " << flag2 << std::endl;

// 使用boolalpha显示为true或false
std::cout << "字母表示: " << std::boolalpha << flag1 << " " << flag2 << std::endl;

// 切换回数字表示
std::cout << "数字表示: " << std::noboolalpha << flag1 << " " << flag2 << std::endl;

return 0;
}

输出结果:

默认布尔值: 1 0
字母表示: true false
数字表示: 1 0

其他常用操纵算子

endl - 插入换行并刷新流

cpp
#include <iostream>

int main() {
std::cout << "Hello" << std::endl;
std::cout << "World" << std::endl;
return 0;
}

输出结果:

Hello
World

flush - 刷新流

cpp
#include <iostream>

int main() {
std::cout << "数据将立即发送到屏幕" << std::flush;
// ... 继续执行其他操作 ...
return 0;
}

showpos 和 noshowpos - 控制正数符号显示

cpp
#include <iostream>

int main() {
int posNum = 42;
int negNum = -42;

std::cout << "默认情况: " << posNum << " " << negNum << std::endl;
std::cout << "显示正号: " << std::showpos << posNum << " " << negNum << std::endl;
std::cout << "不显示正号: " << std::noshowpos << posNum << " " << negNum << std::endl;

return 0;
}

输出结果:

默认情况: 42 -42
显示正号: +42 -42
不显示正号: 42 -42

自定义操纵算子

你也可以创建自己的流操纵算子来满足特定的格式化需求。

cpp
#include <iostream>
#include <iomanip>

// 自定义流操纵算子:输出分隔线
std::ostream& line(std::ostream& os) {
return os << "\n---------------------------\n";
}

// 自定义带参数的流操纵算子
class Repeat {
public:
Repeat(char c, int n) : ch(c), count(n) {}
friend std::ostream& operator<<(std::ostream& os, const Repeat& r) {
for (int i = 0; i < r.count; ++i) {
os << r.ch;
}
return os;
}
private:
char ch;
int count;
};

int main() {
// 使用自定义操纵算子
std::cout << "标题" << line << "内容" << line;

// 使用自定义带参数的操纵算子
std::cout << "星号: " << Repeat('*', 20) << std::endl;

return 0;
}

输出结果:

标题
---------------------------
内容
---------------------------
星号: ********************

实际应用案例

案例1:格式化表格输出

cpp
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>

struct Product {
std::string name;
double price;
int quantity;
};

void printProductTable(const std::vector<Product>& products) {
// 设置标题
std::cout << std::left
<< std::setw(20) << "产品名称"
<< std::setw(10) << "价格"
<< std::setw(10) << "数量"
<< std::setw(15) << "总价"
<< std::endl;

// 输出分隔线
std::cout << std::setfill('-') << std::setw(55) << "" << std::endl;
std::cout << std::setfill(' '); // 重置填充字符

double totalAmount = 0.0;

// 输出每个产品的信息
for (const auto& product : products) {
double itemTotal = product.price * product.quantity;
totalAmount += itemTotal;

std::cout << std::left << std::setw(20) << product.name
<< std::right << std::setw(9) << std::fixed << std::setprecision(2) << product.price << " "
<< std::setw(9) << product.quantity << " "
<< std::setw(14) << itemTotal << std::endl;
}

// 输出分隔线和总计
std::cout << std::setfill('-') << std::setw(55) << "" << std::endl;
std::cout << std::setfill(' '); // 重置填充字符

std::cout << std::right << std::setw(41) << "总计: "
<< std::setw(14) << std::fixed << std::setprecision(2) << totalAmount << std::endl;
}

int main() {
std::vector<Product> products = {
{"键盘", 299.99, 2},
{"鼠标", 99.50, 3},
{"显示器", 1299.99, 1}
};

printProductTable(products);

return 0;
}

输出结果:

产品名称                价格        数量        总价          
-------------------------------------------------------
键盘 299.99 2 599.98
鼠标 99.50 3 298.50
显示器 1299.99 1 1299.99
-------------------------------------------------------
总计: 2198.47

案例2:生成配置文件

cpp
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <map>

void generateConfigFile(const std::string& filename, const std::map<std::string, std::string>& config) {
std::ofstream file(filename);

if (!file) {
std::cerr << "无法创建配置文件!" << std::endl;
return;
}

// 设置文件头
file << "# 配置文件生成于: " << std::put_time(std::localtime(&std::time_t(std::time(nullptr))), "%Y-%m-%d %H:%M:%S") << std::endl;
file << "# 请勿手动编辑此文件" << std::endl << std::endl;

// 写入配置项
for (const auto& item : config) {
file << std::left << std::setw(30) << item.first << " = " << item.second << std::endl;
}

file.close();
std::cout << "配置文件已生成: " << filename << std::endl;
}

int main() {
std::map<std::string, std::string> configSettings = {
{"server.host", "localhost"},
{"server.port", "8080"},
{"database.connection", "mysql://user:pass@localhost:3306/mydb"},
{"logging.level", "INFO"},
{"app.debug_mode", "false"}
};

generateConfigFile("app_config.ini", configSettings);

return 0;
}

生成的配置文件内容可能如下:

# 配置文件生成于: 2023-10-31 15:30:45
# 请勿手动编辑此文件

server.host = localhost
server.port = 8080
database.connection = mysql://user:pass@localhost:3306/mydb
logging.level = INFO
app.debug_mode = false

总结

C++流操纵算子是一组强大的工具,可以帮助你格式化和控制输入输出流的行为。通过使用这些操纵算子,你可以:

  • 控制数值的进制表示(十进制、十六进制、八进制)
  • 设置对齐方式和填充字符
  • 控制浮点数的精度和表示格式
  • 自定义输出的布局和样式
  • 创建专业和易读的输出格式

熟练掌握流操纵算子的使用,将大大提高你的C++程序的可读性和专业性。

练习

  1. 编写一个程序,使用流操纵算子格式化输出一个包含姓名、年龄和工资的员工信息表。
  2. 实现一个自定义流操纵算子,用于输出带有时间戳的日志消息。
  3. 编写一个程序,使用流操纵算子格式化输出一个包含不同进制(二进制、十进制、十六进制)的数值表格。
  4. 创建一个简单的财务报表程序,使用流操纵算子格式化金额和百分比。
  5. 编写一个程序,读取CSV格式的数据,并使用流操纵算子将其格式化输出为对齐良好的表格。

附加资源

  • C++标准库中的 <iomanip> 头文件文档
  • 更多关于C++流和I/O操作的资源
  • 《C++ Primer》和《The C++ Programming Language》等书籍中有关流操纵算子的章节