C++:动态刷新打印内容

news2025/5/24 17:28:26

目录

  • 1.简介
    • 1.1 Display类原理简述
  • 2.代码
    • 2.1 main.cpp:无注释版
    • 2.2 main.cpp:有注释版
  • 3.编译运行


1.简介

本文介绍一个用于命令行动态覆盖输出的C++实现(Display类);

效果说明:

  • 普通输出会直接换行显示。

  • 分段输出(如进度条)会在同一行动态刷新,模拟进度条效果。

  • 中文内容也能正确显示和自动换行。

  • 你可以修改max_word_per_line参数,观察自动换行效果。

.

1.1 Display类原理简述

  • Display类用于在命令行中动态刷新输出内容,适合进度条、分段日志等场景。

  • 支持中英文混合输出,自动换行。

  • 在Windows和Linux/macOS下分别做了兼容性处理。

  • 通过Print(int32_t segment_id, const std::string &s)方法输出内容:

    • segment_id为-1时为普通输出,其他值用于分段刷新。

    • 同一段内容会被“覆盖式”刷新,不同段内容会换行显示。

.

2.代码

2.1 main.cpp:无注释版

#include <iostream>
#include <string>
#include <cstdio>
#include <thread>
#include <chrono>

class Display {
 public:
  explicit Display(int32_t max_word_per_line = 20)
      : max_word_per_line_(max_word_per_line) {}

  virtual void Print(int32_t segment_id, const std::string &s) {
#ifdef _MSC_VER
    if (segment_id != -1) {
      if (last_segment_ != segment_id) {
          fprintf(stderr, "\n%d:%s", segment_id, s.c_str());
          last_segment_ = segment_id;
      } else {
          fprintf(stderr, "\r%d:%s", segment_id, s.c_str());
      }
    } else {
      fprintf(stderr, "%s\n", s.c_str());
    }
    return;
#endif
    if (last_segment_ == segment_id) {
      Clear();
    } else {
      if (last_segment_ != -1) {
        fprintf(stderr, "\n\r");
      }
      last_segment_ = segment_id;
      num_previous_lines_ = 0;
    }

    if (segment_id != -1) {
      fprintf(stderr, "\r%d:", segment_id);
    }

    int32_t i = 0;
    for (size_t n = 0; n < s.size();) {
      if (s[n] > 0 && s[n] < 0x7f) {
        fprintf(stderr, "%c", s[n]);
        ++n;
      } else {
        std::string tmp(s.begin() + n, s.begin() + n + 3);
        fprintf(stderr, "%s", tmp.data());
        n += 3;
      }

      ++i;
      if (i >= max_word_per_line_ && n + 1 < s.size() &&
          (s[n] == ' ' || s[n] < 0)) {
        fprintf(stderr, "\n\r ");
        ++num_previous_lines_;
        i = 0;
      }
    }
  }

 private:
  void Clear() {
    fprintf(stderr, "\33[2K\r");
    while (num_previous_lines_ > 0) {
      fprintf(stderr, "\033[1A\r");
      fprintf(stderr, "\33[2K\r");
      --num_previous_lines_;
    }
  }

 private:
  int32_t max_word_per_line_;
  int32_t num_previous_lines_ = 0;
  int32_t last_segment_ = -1;
};

int main() {
    Display display(20);

    display.Print(-1, "Hello, this is a normal output.");
    std::this_thread::sleep_for(std::chrono::seconds(1));

    for (int i = 0; i <= 5; ++i) {
        display.Print(1, "Progress: " + std::to_string(i * 20) + "%");
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }

    display.Print(2, "这是一个中文测试,看看能否正确换行和显示。");
    std::this_thread::sleep_for(std::chrono::seconds(1));

    display.Print(1, "Progress: 100% 完成!");
    std::this_thread::sleep_for(std::chrono::seconds(1));

    display.Print(-1, "All done!");

    return 0;
}

2.2 main.cpp:有注释版

#include <iostream>
#include <string>
#include <cstdio>
#include <thread>
#include <chrono>

// 用于命令行动态刷新输出的Display类
class Display {
 public:
  // 构造函数,设置每行最大字符数,默认20
  explicit Display(int32_t max_word_per_line = 20)
      : max_word_per_line_(max_word_per_line) {}

  // 动态输出函数
  // segment_id: 段编号(-1表示普通输出,其他值用于分段刷新)
  // s: 要输出的字符串
  virtual void Print(int32_t segment_id, const std::string &s) {
#ifdef _MSC_VER
    // =========================
    // Windows下的输出逻辑说明
    // =========================
    // Windows终端对ANSI转义序列支持较差,因此采用简单的回车和换行控制输出。
    // 如果segment_id不是-1,表示需要分段输出(如进度条等):
    if (segment_id != -1) {
      // 如果当前段编号和上一次不同,先换行再输出新内容,并更新last_segment_
      if (last_segment_ != segment_id) {
          fprintf(stderr, "\n%d:%s", segment_id, s.c_str());
          last_segment_ = segment_id;
      } else {
          // 如果是同一段,直接用回车覆盖当前行内容
          fprintf(stderr, "\r%d:%s", segment_id, s.c_str());
      }
    } else {
      // segment_id为-1,表示普通输出,直接输出字符串并换行
      fprintf(stderr, "%s\n", s.c_str());
    }
    // Windows下不支持多行清除,直接返回
    return;
#endif
    // =========================
    // Linux/macOS下的输出逻辑
    // =========================
    // 如果是同一段,清除之前的输出(为刷新做准备)
    if (last_segment_ == segment_id) {
      Clear();
    } else {
      // 如果是新段落,先换行
      if (last_segment_ != -1) {
        fprintf(stderr, "\n\r");
      }
      last_segment_ = segment_id;
      num_previous_lines_ = 0;
    }

    // 如果是分段输出,先输出段编号
    if (segment_id != -1) {
      fprintf(stderr, "\r%d:", segment_id);
    }

    int32_t i = 0; // 当前行已输出字符数
    for (size_t n = 0; n < s.size();) {
      if (s[n] > 0 && s[n] < 0x7f) {
        // 英文字符直接输出
        fprintf(stderr, "%c", s[n]);
        ++n;
      } else {
        // 中文字符(UTF-8下3字节)特殊处理
        std::string tmp(s.begin() + n, s.begin() + n + 3);
        fprintf(stderr, "%s", tmp.data());
        n += 3;
      }

      ++i;
      // 达到最大行宽且下一个字符是空格或特殊字符时换行
      if (i >= max_word_per_line_ && n + 1 < s.size() &&
          (s[n] == ' ' || s[n] < 0)) {
        fprintf(stderr, "\n\r ");
        ++num_previous_lines_;
        i = 0;
      }
    }
  }

 private:
  // 清除当前段的输出
  void Clear() {
    // 清除当前行:ClearCurrentLine()
    fprintf(stderr, "\33[2K\r");
    // 如果有多行输出,逐行向上清除
    while (num_previous_lines_ > 0) {
      // 光标上移一行:GoUpOneLine()
      fprintf(stderr, "\033[1A\r");
      // 清除当前行:ClearCurrentLine()
      fprintf(stderr, "\33[2K\r");
      --num_previous_lines_;
    }
  }

 private:
  int32_t max_word_per_line_;      // 每行最大字符数
  int32_t num_previous_lines_ = 0; // 之前输出的行数
  int32_t last_segment_ = -1;      // 上一次输出的段编号
};

int main() {
    Display display(20);

    // 普通输出
    display.Print(-1, "Hello, this is a normal output.");
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // 段落1,模拟进度刷新
    for (int i = 0; i <= 5; ++i) {
        display.Print(1, "Progress: " + std::to_string(i * 20) + "%");
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }

    // 段落2,输出中文
    display.Print(2, "这是一个中文测试,看看能否正确换行和显示。");
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // 段落1,再次刷新
    display.Print(1, "Progress: 100% 完成!");
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // 普通输出
    display.Print(-1, "All done!");

    return 0;
}

3.编译运行

# 在Linux/macOS上
g++ -std=c++11 -o main main.cpp
./main

# 在Windows(PowerShell/CMD)上
g++ -std=c++11 -o main.exe main.cpp
.\main.exe

注意:

  • Windows下建议使用PowerShell或WSL终端,CMD对ANSI转义序列支持较差,刷新效果可能不理想。

  • 中文输出需保证终端支持UTF-8编码。

.


声明:资源可能存在第三方来源,若有侵权请联系删除!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2384758.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络学习-TCP协议(七)

一、TCP协议 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议。 1、三次握手 客户端&#xff1a; 1、先发起连接&#xff0c;发送SYN置1&#xff0c;seqnum12345(随机值)----半连接…

基于微信小程序的高校校园微活动管理系统设计与实现(源码+定制+开发)高校微信小程序校园活动发布与互动平台开发 面向大学生群体的校园活动移动平台设计与实现

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

云计算与大数据进阶 | 27、存储系统如何突破容量天花板?可扩展架构的核心技术与实践—— 分布式、弹性扩展、高可用的底层逻辑(上)

数据中心里&#xff0c;存储系统是至关重要的组成部分。由于相关硬件组件与存储操作系统的多样性和复杂性&#xff0c;如何在保证存储稳定、安全、可靠的同时&#xff0c;实现灵活扩展和自服务&#xff0c;一直是困扰数据中心全面云化的难题。 简单来说&#xff0c;现在的难题…

IvorySQL-WASM:免安装的数据库探索之旅

简介 为了降低社区用户的使用门槛&#xff0c;提升使用体验&#xff0c;IvorySQL 社区特别推出了 IvorySQL-WASM 项目&#xff0c;帮助用户快速在线 Demo。 IvorySQL-WASM 基于开源的 Postgres-WASM 框架开发。它允许用户直接在网页浏览器中体验 IvorySQL&#xff0c;无需本地…

飞牛fnNAS远程映射盘符

目录 一、NAS、PC端配置Zerotier 二、使用网上邻居 三、使用WebDAV 1.开启WebDAV 2.PC上安装RaiDrive并设置 如果能将NAS作为本机一个盘符来使用,一定会令我非常方便。如果是本地,可以很方便实现。 将飞牛NAS映射为本地盘符,常用两种方式,一种是网上邻居,另一种是We…

Java设计模式:探索编程背后的哲学

设计模式是软件开发中的一种常见方法&#xff0c;它为常见问题提供了解决方案。在Java世界中&#xff0c;设计模式的应用尤为广泛。本文将深入探讨Java设计模式的起源、分类和实际应用&#xff0c;帮助读者更好地理解和应用这些模式。设计模式不仅是编程的技术&#xff0c;更是…

Docker部署OpenSearch集群

OpenSearch 简介 OpenSearch 是一款开源的搜索与分析引擎&#xff0c;最初由亚马逊 AWS 开发&#xff0c;于 2021 年 9 月将其移交至 Linux 基金会旗下的 OpenSearch 软件基金会&#xff0c;此后实现了社区主导的治理模式。其具有高性能、可扩展性强、兼容性强等优点&#xff…

【AS32X601驱动系列教程】PLIC_中断应用详解

平台中断控制器&#xff08;Platform Level Interrupt Controller&#xff0c;PLIC&#xff09;是国科安芯AS32系列MCU芯片的中断控制器&#xff0c;主要对中断源进行采样&#xff0c;优先级仲裁和分发。各外设中断统一连到PLIC&#xff0c;PLIC统一管理并输出中断请求到内核。…

python学习打卡day34

DAY 34 GPU训练及类的call方法 知识点回归&#xff1a; CPU性能的查看&#xff1a;看架构代际、核心数、线程数GPU性能的查看&#xff1a;看显存、看级别、看架构代际GPU训练的方法&#xff1a;数据和模型移动到GPU device上类的call方法&#xff1a;为什么定义前向传播时可以直…

leetcode-快慢指针系列

开胃小菜 141. 环形链表 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链…

JAVA05基本数据类型和包装类的转换,转换成其他数据类型,包装类与字符串的转换+学生类的定义实例

1.基本数据类型和包装类的转换 下面是一个自动手动的例题 2.将包装类转换成其他类型 3. 将数据类型转换成字符串 将字符串转换成数据类型 以下是一个例题 学生类的例题

Python打卡训练营学习记录Day34

知识点回归&#xff1a; CPU性能的查看&#xff1a;看架构代际、核心数、线程数 GPU性能的查看&#xff1a;看显存、看级别、看架构代际 GPU训练的方法&#xff1a;数据和模型移动到GPU device上 类的call方法&#xff1a;为什么定义前向传播时可以直接写作self.fc1(x) CPU性…

动手学习深度学习V1.1 chapter2 (2.1-2.2)

chapter2&#xff1a;深度学习基础 区分问题&#xff1a;回归问题还是分类问题&#xff1f; 输出结果是不明确的连续值的时候就是回归问题&#xff0c;比如房价预测&#xff0c;销售额预测等。 输出结果是明确几个离散值的时候就是分类问题&#xff0c;比如字符识别&#xf…

数据结构(6)线性表-队列

一、队列的概述 队列也是一种特殊的线性表&#xff0c;只允许在一段插入数据&#xff0c;另一端删除数据。插入操作的一端称为队尾&#xff0c;删除操作的一端称为队头。 如图&#xff1a; 二、队列相关操作 1.队列结构体的声明 类似于栈&#xff0c;他肯定也得借助于数组或…

【数据架构04】数据湖架构篇

✅ 10张高质量数据治理架构图 无论你是数据架构师、治理专家&#xff0c;还是数字化转型负责人&#xff0c;这份资料库都能为你提供体系化参考&#xff0c;高效解决“架构设计难、流程不清、平台搭建慢”的痛点&#xff01; &#x1f31f;限时推荐&#xff0c;速速收藏&#…

uniapp-商城-62-后台 商品列表(分类展示商品的布局)

每一个商品都有类别&#xff0c;比如水果&#xff0c;蔬菜&#xff0c;肉&#xff0c;粮油等等&#xff0c;另外每一个商品都有自己的属性&#xff0c;这些都在前面的章节进行了大量篇幅的介绍。这里我们终于完成了商品类的添加&#xff0c;商品的添加&#xff0c;现在到了该进…

初识C++:模版

本篇博客主要讲解C模版的相关内容。 目录 1.泛型编程 2.函数模板 2.1 函数模版概念 2.2 函数模版格式 2.3 函数模版的原理 2.4 函数模版的实例化 1.隐式实例化&#xff1a;让编译器根据实参推演模板参数的实际类型 2. 显式实例化&#xff1a;在函数名后的<>中指定模…

突破认知边界:神经符号AI的未来与元认知挑战

目录 一、神经符号AI的核心领域与研究方法 &#xff08;一&#xff09;知识表示&#xff1a;构建智能世界的语言 &#xff08;二&#xff09;学习与推理&#xff1a;让机器“思考”与“学习” &#xff08;三&#xff09;可解释性与可信度&#xff1a;让AI更透明 &#xf…

Java 处理地理信息数据[DEM TIF文件数据获取高程]

目录 1、导入依赖包 2、读取方法 3、其他相关地理信息相关内容&#xff1a; 1️⃣常用的坐标系 1、GIS 中的坐标系一般分为两大类&#xff1a; 2. ✅常见的地理坐标系 2.0 CGCS2000&#xff08;EPSG:4490&#xff09; 2.1 WGS84 (World Geodetic System 1984) &#xff08;EPSG…

谈谈对dubbo的广播机制的理解

目录 1、介绍 1.1、广播调用 1、工作原理 1.2、调用方式 1、Reference 注解 2、XML 配置 3、全局配置 1.3、 广播机制的特性 2、重试机制 2.1、默认行为 2.2、自定义逻辑 1、在业务层封装重试逻辑 2、使用 Reference 3、广播调用的实践 3.1、常用参数 1.…