C++流类库 概述及流的格式化输入/输出控制

第一部分:C++流类库概述与体系结构

C++没有内置的输入/输出语句,而是通过一个强大的I/O流类库 来实现。这个库的核心思想是"" ------ 一种在数据的生产者(如键盘、文件)和消费者(如程序、屏幕、文件)之间建立的、管理数据流动的抽象。

1. 核心流类体系

整个流类体系是一个派生类体系。下面是主要的类及其关系:

复制代码
                                    ios_base (抽象基类,包含格式状态和操作)
                                           |
                                    basic_ios<> (模板基类,管理流缓冲区)
                                    /                  \
                                  /                      \
                                /                          \
                    basic_ostream<> (输出流)        basic_istream<> (输入流)
                            |                               |
                    (插入操作 <<)                    (提取操作 >>)
                            |                               |
                    -------------------  -------------------
                    |                                  |
            basic_ofstream<>                  basic_ifstream<>
            (文件输出流)                       (文件输入流)
                    |                                  |
            -------------------  -------------------
                    |                                  |
            basic_ostringstream<>            basic_istringstream<>
            (字符串输出流)                    (字符串输入流)
                    |                                  |
                    ------------------------------------
                                   |
                        basic_stringstream<> / basic_fstream<>
                        (字符串输入输出流)   / (文件输入输出流)

重要具体类解释(对应常用类型别名):

  • ios : 通常指ios_basebasic_ios,是所有流类的抽象根基,定义了格式控制、状态标志等公共接口。

  • ostream (std::ostream) : 通用输出流类。coutclog 就是它的对象。

  • istream (std::istream) : 通用输入流类。cin 就是它的对象。

  • iostream : 多重继承自istreamostream,用于需要同时进行输入输出的场景。

  • ofstream: 专用于文件输出的类。

  • ifstream: 专用于文件输入的类。

  • fstream: 专用于文件输入输出的类。

  • ostringstream: 用于向内存中的字符串写入数据。

  • istringstream: 用于从内存中的字符串读取数据。

  • stringstream: 用于对内存中的字符串进行读写。

  • streambuf流缓冲区类 。这是一个关键但常被忽略的类。ios类内部包含一个指向streambuf的指针。它负责底层字符序列的实际读写、缓冲管理。文件操作对应filebuf,字符串操作对应stringbuf

四个预定义的全局流对象:

cpp

复制代码
#include <iostream>
std::cin;   // 标准输入,对应键盘,带缓冲的istream对象
std::cout;  // 标准输出,对应屏幕,带缓冲的ostream对象
std::cerr;  // 标准错误输出,对应屏幕,无缓冲(立即输出)
std::clog;  // 标准日志输出,对应屏幕,带缓冲

运算符重载:

  • operator <<插入运算符 ,用于输出。cout << data;

  • operator >>提取运算符 ,用于输入。cin >> data;


第二部分:流的格式化输入/输出控制

控制数据如何显示(如进制、宽度、精度、对齐)是I/O操作的核心。主要有三种方式。

2.1 方式一:使用 ios 基类的成员函数

ios类内部通过一组标志位(flags)成员变量来控制格式。

主要格式标志位(ios::fmtflags类型):

这些标志在ios_base中定义为静态常量或枚举值。

cpp

复制代码
// 进制控制
ios::dec        // 十进制 (默认)
ios::oct        // 八进制
ios::hex        // 十六进制
ios::showbase   // 显示进制前缀(0, 0x)

// 浮点数显示
ios::fixed      // 固定小数格式
ios::scientific // 科学计数法格式
ios::showpoint  // 总是显示小数点
ios::showpos    // 非负数显示 + 号

// 对齐与填充
ios::left       // 左对齐
ios::right      // 右对齐 (默认)
ios::internal   // 符号左对齐,数值右对齐(用于数字)

// 其他
ios::skipws     // 输入时跳过空白字符 (默认)
ios::uppercase  // 十六进制输出使用大写字母,科学计数法输出'E'
ios::boolalpha  // 将bool值输出为 true/false 而非 1/0

// 刷新控制
ios::unitbuf    // 每次输出后立即刷新缓冲区

相关成员函数:

cpp

复制代码
// 1. 获取/设置全部标志字
fmtflags flags() const;             // 获取当前所有格式标志
fmtflags flags(fmtflags fmtfl);    // 设置新标志,返回旧标志

// 2. 设置特定标志位
fmtflags setf(fmtflags fmtfl);                 // 添加(开启)标志位
fmtflags setf(fmtflags fmtfl, fmtflags mask); // 在mask指定的区域中设置fmtfl
// mask 常用值: ios::basefield (dec|oct|hex), ios::adjustfield (left|right|internal), ios::floatfield (scientific|fixed)

// 3. 清除特定标志位
void unsetf(fmtflags fmtfl);

// 4. 控制域宽、填充字符、浮点精度
streamsize width() const;          // 获取当前域宽
streamsize width(streamsize wide); // 设置域宽,仅对下一次IO有效
char fill() const;                 // 获取当前填充字符
char fill(char fillch);            // 设置填充字符
streamsize precision() const;      // 获取当前浮点数精度
streamsize precision(streamsize prec); // 设置浮点数精度

示例:使用成员函数控制格式

cpp

复制代码
#include <iostream>
int main() {
    int num = 255;
    double pi = 3.1415926535;

    // 设置十六进制并显示前缀
    std::cout.setf(std::ios::hex | std::ios::showbase | std::ios::uppercase);
    std::cout << num << std::endl; // 输出: 0xFF

    // 清除十六进制,恢复十进制
    std::cout.unsetf(std::ios::hex | std::ios::showbase | std::ios::uppercase);
    std::cout.setf(std::ios::dec);
    std::cout << num << std::endl; // 输出: 255

    // 设置域宽、填充、左对齐
    std::cout.width(10);
    std::cout.fill('*');
    std::cout.setf(std::ios::left);
    std::cout << num << std::endl; // 输出: 255*******

    // 设置浮点数精度和格式
    std::cout.precision(5);
    std::cout.setf(std::ios::fixed);
    std::cout << pi << std::endl; // 输出: 3.14159

    return 0;
}
2.2 方式二:使用标准流操纵符(Manipulators)

这是更简洁、更常用的方式。操纵符是特殊的函数或对象,可以直接用在<<>>运算符链中。

非参数化操纵符(定义在<ios><iostream>中):

cpp

复制代码
// 进制
std::dec, std::oct, std::hex

// 显示控制
std::showbase, std::noshowbase
std::uppercase, std::nouppercase
std::showpoint, std::noshowpoint
std::showpos, std::noshowpos
std::boolalpha, std::noboolalpha

// 对齐
std::left, std::right, std::internal

// 浮点格式
std::fixed, std::scientific, std::defaultfloat (恢复默认)

// 刷新与跳过空白
std::endl    // 插入换行并刷新缓冲区
std::flush   // 仅刷新缓冲区
std::ws      // 输入时跳过空白字符 (用于cin >> std::ws;)

示例:使用非参数化操纵符

cpp

复制代码
#include <iostream>
int main() {
    int num = 255;
    bool flag = true;

    std::cout << std::hex << std::showbase << std::uppercase << num << std::endl; // 0xFF
    std::cout << std::boolalpha << flag << std::noboolalpha << std::endl; // true
    std::cout << std::dec << num << std::endl; // 255
    return 0;
}
2.3 方式三:使用参数化流操纵符(需包含<iomanip>

这些操纵符可以接受参数,功能更强大。

cpp

复制代码
#include <iomanip>

std::setw(int n)        // 设置域宽 (类似width(),只影响下一次IO)
std::setfill(char c)    // 设置填充字符
std::setprecision(int n)// 设置浮点数精度
std::setbase(int b)     // 设置整数进制 (b=8,10,16)
// C++98 之后还有 setiosflags, resetiosflags 等,但不如直接使用标志方便

示例:综合使用参数化操纵符

cpp

复制代码
#include <iostream>
#include <iomanip>
int main() {
    int num = 12345;
    double money = 1234.567;

    std::cout << std::setw(15) << std::setfill('_') << std::left << num << std::endl; // 12345__________
    std::cout << std::setw(12) << std::setfill(' ') << std::fixed << std::setprecision(2) << money << std::endl; //     1234.57
    std::cout << std::setbase(16) << std::showbase << 100 << std::endl; // 0x64
    return 0;
}

这部分我们涵盖了C++流类库的基本框架和格式化控制。核心要点

  1. 理解流类体系的层次关系 ,特别是istreamostream及其文件/字符串特化版本。

  2. 掌握格式控制的三种方式 :成员函数、非参数操纵符、参数化操纵符。在代码中,最推荐使用操纵符(尤其是<iomanip>中的),因为它们更清晰、更容易嵌入到输出链中。

相关推荐
稚辉君.MCA_P8_Java2 小时前
在Java中,将`Short`(包装类)或`short`(基本类型)转换为`int`
java·开发语言
木易 士心2 小时前
Node.js 性能诊断利器 Clinic.js:原理剖析与实战指南
开发语言·javascript·node.js
一只乔哇噻2 小时前
java后端工程师+AI大模型进修ing(研一版‖day59)
java·开发语言·算法·语言模型
天赐学c语言2 小时前
12.2 - LRU缓存 && C语言内存布局
c++·算法·lru·内存布局
2301_789015622 小时前
C++:list(带头双向链表)增删查改模拟实现
c语言·开发语言·c++·list
扣脚大汉在网络2 小时前
关于一句话木马
开发语言·网络安全
暗然而日章2 小时前
C++基础:Stanford CS106L学习笔记 5 内存与指针
c++·笔记·学习
学习路上_write2 小时前
FREERTOS_定时器——创建和基本使用
c语言·开发语言·c++·stm32·嵌入式硬件
学技术的大胜嗷2 小时前
如何在 VSCode 中高效开发和调试 C++ 程序:面向用过 Visual Studio 的小白
c++·vscode·visual studio