本文介绍如何使用插入运算符、如何控制输出格式,以及如何为自定义类重载插入运算符。
目录
- [1 概述](#1 概述)
- [2 输出宽度](#2 输出宽度)
-
- [2.1 width 示例(默认空格填充)](#2.1 width 示例(默认空格填充))
- [2.2 width 示例(自定义填充字符)](#2.2 width 示例(自定义填充字符))
- [2.3 setw 示例](#2.3 setw 示例)
- [2.4 规则](#2.4 规则)
- [2.5 综述](#2.5 综述)
- [3 对齐方式](#3 对齐方式)
-
- [3.1 示例](#3.1 示例)
- [3.2 常用对齐标识](#3.2 常用对齐标识)
- [3.3 简化写法(C++标准操纵符,无需 setiosflags)](#3.3 简化写法(C++标准操纵符,无需 setiosflags))
- [4 精度控制](#4 精度控制)
-
- [4.1 示例1(无标记)](#4.1 示例1(无标记))
- [4.2 示例2(定点标记)](#4.2 示例2(定点标记))
- [4.3 示例3(科学计数法)](#4.3 示例3(科学计数法))
- [4.4 规则](#4.4 规则)
- [4.5 简化写法](#4.5 简化写法)
- [5 进制基数](#5 进制基数)
-
- [5.1 示例(大小写控制)](#5.1 示例(大小写控制))
- [5.2 规则](#5.2 规则)
- [6 带引号字符串操纵符(C++14)](#6 带引号字符串操纵符(C++14))
1 概述
插入运算符 << 已预先为所有 C++标准数据类型实现,作用是将字节数据写入输出流对象 。插入运算符可配合预定义的流操纵符使用,操纵符能够修改整数的默认输出格式。
你可以通过以下四类配置控制输出样式:
输出宽度
- setw(n),设置单次输出占位字符长度;
对齐方式
- left / right / internal,左对齐、右对齐、符号内对齐;
浮点数精度
- setprecision(n),控制小数保留位数或有效数字;
数值进制基数
- dec(十进制)、hex(十六进制)、oct(八进制);
说明插入运算符 <<
- 内置支持 int、double、string、bool 等基础类型;自定义结构体 / 类需要自己重载 operator<< 才能直接用 cout << obj。
操纵符 manipulator
- 如 setw、left、fixed、hex 这类,写在 << 中间,全局修改流的打印规则。
2 输出宽度
想要对齐输出内容,可以通过两种方式为每个输出项指定字段宽度:
- 在流中插入
setw操纵符;- 调用流的
width成员函数;
2.1 width 示例(默认空格填充)
下面示例将所有数值右对齐,每个字段至少占用 10 个字符宽度:
cpp
// output_width.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main( )
{
double values[] = { 1.23, 35.36, 653.7, 4358.24 };
for( int i = 0; i < 4; i++ )
{
cout.width(10);
cout << values[i] << '\n';
}
}
运行结果:
可以看到,在输出结果中,长度不足10个字符的数值,左侧会自动填充空格。
2.2 width 示例(自定义填充字符)
如果需要自定义填充字符,
使用 fill 成员函数设置填充符号,默认填充字符为空格。修改上面循环,使用星号填充空白区域:
cpp
// output_width.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main()
{
double values[] = { 1.23, 35.36, 653.7, 4358.24 };
for (int i = 0; i < 4; i++)
{
cout.width(10);
cout.fill('*');
cout << values[i] << endl;
}
}
运行结果:
注:操纵符 endl 等价于换行符 '\n' 并刷新缓冲区。
2.3 setw 示例
同一行打印多组数据时,
推荐使用 setw 操纵符连续设置宽度:下述示例中名字占用7字符宽度,数值占用10字符宽度:
cpp
// setw.cpp
// compile with: /EHsc
#include <iostream>
#include <iomanip>
using namespace std;
int main( )
{
double values[] = { 1.23, 35.36, 653.7, 4358.24 };
const char *names[] = { "Zoot", "Jimmy", "Al", "Stan" };
for( int i = 0; i < 4; i++ )
cout << setw( 7 ) << names[i]
<< setw( 10 ) << values[i] << endl;
}
运行结果:
width 成员函数定义在头文件<iostream>;带参数的操纵符 setw 必须引入 <iomanip>。
2.4 规则
- setw 和 width 不会截断输出内容。如果数据实际长度超过设定宽度,完整数值照常输出,仅受精度设置约束。
- setw、width 仅对紧随其后的下一个输出字段生效,打印完一项后宽度自动恢复默认自适应模式。
- 其余格式化配置(填充字符、精度、进制等)一旦设置会持续生效,必须手动修改才会改变。
2.5 综述
- width() 与 setw() 区别
- cout.width(n):成员函数,单独调用,一次只控制后一个输出;仅需 <iostream>;
- setw(n):链式操纵符,写在 << 中间,适合一行多列排版;必须引入 <iomanip>;
- fill() 持久生效
- cout.fill('*') 设置后永久有效,不会像 width 一样一次性失效,直到再次调用 fill 修改;
- 不会截断是核心特性
- 宽度仅用来预留空位对齐,不是限制最大输出长度;
- 数字、字符串过长会完整打印,不会被截断;
3 对齐方式
- 通过操纵符
setiosflags搭配枚举常量 ios::left 开启左对齐。该枚举定义在 ios 类中,使用时必须加上 ios:: 作用域前缀;- 操纵符
resetiosflags用于关闭左对齐标记;- 和 width、setw 只生效一次不同,setiosflags / resetiosflags 设置的格式标记会
永久保持,直到手动修改。
3.1 示例
输出流默认采用右对齐。如果想要把上一示例中的名称左对齐、数字保持右对齐,可以修改循环代码如下:
cpp
// setw.cpp
// compile with: /EHsc
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double values[] = { 1.23, 35.36, 653.7, 4358.24 };
const char* names[] = { "Zoot", "Jimmy", "Al", "Stan" };
for (int i = 0; i < 4; i++)
cout << setiosflags(ios::left)
<< setw(6) << names[i]
<< resetiosflags(ios::left)
<< setw(10) << values[i] << endl;
}
运行结果:
3.2 常用对齐标识
- ios::left:左对齐,空白填充在右侧;
- ios::right:默认右对齐,空白填充在左侧;
- ios::internal:符号左对齐、数字右对齐(仅对数值生效)。
3.3 简化写法(C++标准操纵符,无需 setiosflags)
left / right 是简化版操纵符,底层等价于 setiosflags,同样持久生效。
cpp
// setw.cpp
// compile with: /EHsc
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double values[] = { 1.23, -35.36, +653.7, -4358.24 };
const char* names[] = { "Zoot", "Jimmy", "Al", "Stan" };
for (int i = 0; i < 4; i++)
cout << left << setw(6) << names[i]
<< right << setw(10) << values[i] << endl;
}
4 精度控制
浮点数默认精度为6位有效数字。例如数值3466.9768默认输出为3466.98。可以通过
setprecision操纵符修改浮点数打印规则,搭配两个关键格式标记:
fixed:定点模式,示例数字输出为 3466.976800;scientific:科学计数法,示例数字输出为 3466977e+003;
4.1 示例1(无标记)
沿用对齐小节的案例,要求浮点数只保留1位有效数字,修改循环代码:
cpp
// setw.cpp
// compile with: /EHsc
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double values[] = { 1.23, 35.36, 653.7, 4358.24 };
const char* names[] = { "Zoot", "Jimmy", "Al", "Stan" };
for (int i = 0; i < 4; i++)
cout << setiosflags(ios::left)
<< setw(6)
<< names[i]
<< resetiosflags(ios::left)
<< setw(10)
<< setprecision(1)
<< values[i]
<< endl;
}
运行结果:
setprecision(1)代表小数点后保留1位
4.2 示例2(定点标记)
如果不想自动启用科学计数法,在循环前开启定点标记:
cpp
// setw.cpp
// compile with: /EHsc
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double values[] = { 1.23, 35.36, 653.7, 4358.24 };
const char* names[] = { "Zoot", "Jimmy", "Al", "Stan" };
cout << setiosflags(ios::fixed); //开启定点标记
for (int i = 0; i < 4; i++)
cout << setiosflags(ios::left)
<< setw(6)
<< names[i]
<< resetiosflags(ios::left)
<< setw(10)
<< setprecision(1)
<< values[i]
<< endl;
}
运行结果:
4.3 示例3(科学计数法)
把标记换成 ios::scientific 则强制使用科学计数法,同样保留1位小数:
cpp
// setw.cpp
// compile with: /EHsc
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
double values[] = { 1.23, 35.36, 653.7, 4358.24 };
const char* names[] = { "Zoot", "Jimmy", "Al", "Stan" };
cout << setiosflags(ios::scientific); //开启科学计数法
for (int i = 0; i < 4; i++)
cout << setiosflags(ios::left)
<< setw(6)
<< names[i]
<< resetiosflags(ios::left)
<< setw(10)
<< setprecision(1)
<< values[i]
<< endl;
}
运行结果:
4.4 规则
- 开启 ios::fixed / ios::scientific 任意一种:setprecision(n) = 小数点后保留 n 位数字;
- 两种标记都不开启(默认浮点模式):setprecision(n) = 整个数值的有效数字总位数;
- setiosflags 开启的定点 / 科学计数法标记会持续生效,需要用 resetiosflags 清除;
- 自动切换科学计数:默认模式下,如果数值量级过大/过小,流会自动切换成科学计数法来满足有效数字要求,定点模式则始终保留小数点,不会自动切换。
4.5 简化写法
无需 setiosflags,可直接使用标准操纵符 fixed / scientific:
cpp
cout << fixed << setprecision(2);
cout << scientific << setprecision(3);
5 进制基数
操纵符 dec(十进制)、oct(八进制)、hex(十六进制) 用于设置输入、输出整数时默认的进制。
- 例如向输出流插入 hex 操纵符后,流会自动把整数的内存二进制值转换成十六进制文本输出。
- 若未开启 uppercase 格式标记(默认状态),十六进制里的字母 a~f 会以小写展示;
- 如果开启 uppercase,则字母变为大写 A~F。
流默认的进制为 dec,也就是十进制。
5.1 示例(大小写控制)
cpp
// setw.cpp
// compile with: /EHsc
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int num = 15;
cout << hex << num << endl; // 输出 f
cout << hex << uppercase << num << endl; // 输出 F
}
运行结果
5.2 规则
- 三个进制操纵符作用:
- dec:十进制(默认)
- oct:八进制
- hex:十六进制
三者均为持久生效,设置后一直保持,直到切换为其他进制。- 进制标记、uppercase 标记
全局持久;
6 带引号字符串操纵符(C++14)
将字符串写入流后,直接调用 stringstream::str() 可以完整取回原始字符串。但如果后续想用提取运算符 >> 把流内容读取到新字符串,结果会不符合预期 ------因为 >> 默认遇到第一个空白字符就停止读取。
示例代码如下:
cpp
std::stringstream ss;
std::string inserted = "This is a sentence.";
std::string extracted;
ss << inserted;
ss >> extracted;
std::cout << inserted; // This is a sentence.
std::cout << extracted; // This
虽然可以手动处理该问题,但为了更方便地实现字符串"写入-读出"无损往返,C++14在头文件 <iomanip> 中新增了 std::quoted 流操纵符。
写入流时:quoted() 会用分隔符(默认双引号")包裹整个字符串,内部自带的引号会用转义符 \ 转义;
读取流时:会持续读取所有字符,直到匹配到末尾的分隔符为止;
分隔符仅存在于流缓冲区中:使用 >> 提取字符串时,首尾引号会被自动剔除,内部转义符还原;
调用 basic_stringstream::str() 获取流内部完整文本时,引号与转义符都会保留;
插入、提取操作对空白字符的处理逻辑和代码里字符串的书写形式无关,因此无论输入是普通字符串还是原始字符串字面量,quoted 都适用。原始字符串内自带的引号、换行、制表符等内容,都能被 quoted() 完整保留。







