C++入门(07)标准输入输出_cout、缓冲、\n endl

8 篇文章 0 订阅

1. 标准输入输出

标准输入输出(Standard Input/Output,简称 I/O) 是计算机程序与用户或外部设备(如键盘、显示器)进行数据交换的基础机制。

I/O 的核心作用是通过特定的设备(通常是键盘和显示器)与程序交互,使得程序可以接收用户输入的数据并向用户展示输出的结果。

在程序运行中:
标准输入:是程序从外部获取数据的通道,通常是键盘输入。
标准输出:是程序将数据展示给用户的通道,通常是屏幕显示。
在这里插入图片描述

2. C++ 中的标准输入输出

在 C++ 语言中,标准输入输出通过 头文件提供的流对象来实现,主要包括 cin、cout、cerr 和 clog。

2.1 流 Stream

理解为数据的有序传输通道。数据在输入时从输入设备流入程序,在输出时从程序流向输出设备。
在 C++ 中,cin、cout 等流对象负责管理这些数据流动过程
在这里插入图片描述

2.2 cout(标准输出)

作用:cout 是标准输出流对象,用于向标准输出设备(通常是屏幕)打印数据。
用法:使用插入运算符 << 将数据输出到屏幕上。可以连续使用多个插入运算符来输出多个数据。

#include <iostream>
using namespace std;

int main() {
    cout << "Hello, World!" << endl;  // 输出字符串并换行
    cout << "The answer is: " << 42 << endl;  // 输出多项内容
    return 0;
}

cout 对象存储了与输出有关的信息的数据成员:显示数据时使用的字段宽度、小数位数、显示整数时采用的计数方法、用来处理输出流的缓冲区的 streambuf 对象的地址。

2.3 cout的缓冲行为

输出时,程序首先填满缓冲区,然后把整块数据传输给显示屏,并清空缓冲区,以备下一批输出使用,这被称为刷新缓冲区(flushing the buffer)。

  1. 缓冲(Buffering) 是为了提高输入输出的效率,是内存中的一个区域
  2. 数据先暂存于缓冲区中,在满足一定条件的时候,例如遇到换行符、缓冲区满、显式刷新时,才真正输出到终端设备
  3. 目的是为了减少频繁的 I/O 操作,因为每次数据从内存输出到屏幕、硬盘等外部设备时,都会涉及系统调用,开销较大
  4. 通过缓冲机制,可以将多次小量的数据输出合并成一次较大的输出操作,减少与设备的交互次数,减少系统调用的开销,从而提高整体性能和效率

在这里插入图片描述

2.3 缓冲区刷新的触发

ostream 类对 cout 对象处理的输出进行缓冲,输出的内容,不会立即发送到目标地址而是被存储在缓冲区中,直到缓冲区填满。

然而对于控制台程序的屏幕输出来说,填充缓冲区的重要性没那么高,程序不必等到缓冲区被填满。例如,将换行符发送到缓冲区后,就可以刷新缓冲区,将缓冲区中的数据立即输出到目标设备(如屏幕、文件等)。

C++标准输出流通常是行缓冲的:

  • 缓冲区满,数据会自动输出并清空缓冲区。
  • 输出流是行缓冲的,遇到换行符 \n(行缓冲)时,换行符会触发缓冲区刷新不意味着彻底清空缓冲区,而是将当前积累的数据送出 std::cout << "Hello, world!\n"; ‘\n’ 会导致 cout 刷新缓冲区,将 “Hello, world!\n” 输出到屏幕上
  • 显式刷新(如使用 std::flush、std::endl),代码命令,显式地强求,立即输出缓冲区的数据到目标设备。例如在程序异常终止,未刷新的数据可能会丢失,显式刷新可使得已有数据输出;再例如,如果是进度指示、错误信息等,显式刷新可以避免输出滞后
  • 程序正常结束,标准输出流会自动刷新,完成所有属于该程序的输出
  • 其他,暂不解释

缓冲模式

行缓冲,通常用于终端(控制台)输出,如 std::cout,在输出 \n 或缓冲区满时触发刷新。
全缓冲,常见于文件输出或其他设备,缓冲区满时刷新
无缓冲,如 stderr,每次输出都直接生效,不存在缓冲行为

2.4 ‘ \n’ 和 endl

\n 是一个换行符,它将换行符插入到输出流中。当输出流是行缓冲模式时(比如终端输出),\n 一般会导致缓冲区刷新,将当前积累的行数据输出到目标设备(如屏幕)

std::endl , 既插入一个换行符又强制刷新缓冲区。它的行为等同于在输出 \n 之后立即调用 std::cout.flush(),将缓冲区所有的数据输出到目标设备。

使用 \n 更轻量,不强制刷新缓冲区,减少了对流的刷新频率
std::endl 输出即时显示

小结

在大部分日常编程和调试场景中,这当中的细微区别影响不大,不必纠结于这两者的差异,按习惯或语义清晰性使用就好了。只有在对输出控制有严格要求或程序性能优化成为极致焦点时,才会有选择的考虑
在这里插入图片描述

3. cout/cerr/clog

cout <<,输出数据(有缓冲)
cerr <<,输出错误信息(无缓冲)
clog <<,输出日志信息(有缓冲)
有缓冲的 cout 和 clog ,输出时减少I/O操作的频率
无缓冲的 cerr 将错误消息立即送出,更快地通知用户关于程序的问题

普通输出、错误消息和日志信息

#include <iostream>
#include <fstream>
#include <string>

int main() {
    std::string filename = "data.txt";
    std::ifstream file(filename);

    // 使用 clog 记录日志信息:Starting program...
    std::clog << "Starting program..." << std::endl;

    // 检查文件是否成功打开
    if (!file.is_open()) {
        // 使用 cerr 输出错误信息
        std::cerr << "Error: Failed to open file '" << filename << "'" << std::endl;
        return 1; // 返回错误码
    }

    std::clog << "File '" << filename << "' opened successfully." << std::endl;

    std::string line;
    int lineNumber = 0;

    // 读取文件内容
    while (std::getline(file, line)) {
        ++lineNumber;

        // 使用 clog 记录读取的每一行的信息
        std::clog << "Processing line " << lineNumber << ": " << line << std::endl;

        // 假设对每一行进行一些简单的处理
        // 使用 cout 输出处理结果
        std::cout << "Processed line " << lineNumber << ": " << line << std::endl;
    }

    // 使用 clog 记录日志信息:程序结束运行
    std::clog << "Program finished successfully." << std::endl;

    return 0;
}

在这里插入图片描述

逻辑分离,不同类型的输出可以独立控制

普通输出、错误消息和日志信息,
各自独立、分开使用不同的流
不同类型的输出可以独立控制

日志输出,可以单独重定向到日志文件
std::clog 输出程序执行过程中的日志信息,如程序开始、文件成功打开、每一行的处理过程以及程序结束。这些日志信息可以在调试过程中帮助了解程序的执行步骤

错误输出,可以输出到标准错误设备
std::cerr 用于输出错误信息,如文件打开失败,提示程序无法继续运行。cerr 的无缓冲特性确保错误信息能第一时间输出

普通输出,可以留给用户查看
std::cout 用于输出程序的正常处理结果,这些是用户真正关心的结果数据
在这里插入图片描述
如果把data.txt 改名为data1.txt,则找不到代码中的“data.txt”
在这里插入图片描述

#include <iostream>
#include <fstream>
#include <string>

int main() {
    // 设置日志输出到文件
    std::ofstream logFile("program.log");
    if (!logFile) {
        std::cerr << "Error: Unable to open log file for writing." << std::endl;
        return 1;
    }

    // 重定向 clog 到 logFile
    std::clog.rdbuf(logFile.rdbuf());

    // 输出日志信息
    std::clog << "Program started..." << std::endl;

    // 模拟读取数据文件
    std::string filename = "data.txt";
    std::ifstream file(filename);

    if (!file.is_open()) {
        // 输出错误信息到标准错误设备
        std::cerr << "Error: Failed to open file '" << filename << "'" << std::endl;
        std::clog << "Failed to open file: " << filename << std::endl;
        return 1;
    }

    std::clog << "File '" << filename << "' opened successfully." << std::endl;

    std::string line;
    int lineNumber = 0;

    // 读取并处理文件内容
    while (std::getline(file, line)) {
        ++lineNumber;
        std::clog << "Processing line " << lineNumber << ": " << line << std::endl;
        std::cout << "Output line " << lineNumber << ": " << line << std::endl; // 普通输出
    }

    std::clog << "Program finished successfully." << std::endl;

    return 0;
}

// 重定向 clog 到 logFile
std::clog.rdbuf(logFile.rdbuf());
这行代码将 clog 的缓冲区重定向到 logFile,实现了将日志信息独立输出到一个文件(program.log)。
所以,不用修改程序的其他逻辑,只要修改重定向的目标(如更改日志文件路径、输出到网络日志服务等)就可以实现分离

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dotdotyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值