`

命令行进度条的实现原理和示例

    博客分类:
  • Tip
 
阅读更多

原理简述

对于通常的命令行输出,大部分人的印象都是一行一行递增的模式。而进度条需要每次内容更新都在同一行,容易让人觉得很“神奇”。一些问答网站上,如 StackOverflow,就有人问“某某语言如何实现命令行进度条”。

 

 

其实命令行进度条与具体编程语言无关,它只是巧用了回车符。(回车符在很多编程语言中是“\r”)即,在更新进度条时并不会另起一行,而是对进度条整行内容进行覆写,而且新的内容以回车符打头,并且末尾没有换行符。(换行符在很多编程语言中是“\n”)

 

Java版实现示例

命令行进度条的实现原理虽然简单,但是具体实现可以变化万千。在具体项目中可以根据需求自行设计。

此示例中,有两个主要的类:

  • ProgressBar:负责更新进度条内容
  • Worker:模拟执行耗时的工作,并触发进度更新

界面示例

 

Worker

class Worker {
  private ProgressBar progressBar;
  private int workload;

  Worker(int workload, ProgressBar progressBar) {
    this.workload = workload;
    this.progressBar = progressBar;
  }

  void start() {
    System.out.print("Working...");

    int progress = 0;
    while (progress < workload) {
      // 用sleep模拟一个耗时的操作
      try {
        Thread.sleep(100);
      } catch (InterruptedException ignore) {
      }

      // 更新进度:每次进度加1
      progress++;
      progressBar.onProgressChanged(progress);
    }

    System.out.print("Worker finish.");
  }
}

 

ProgressBar

class ProgressBar {

  private boolean inited = true;
  private boolean done = false;
  // 共10个进度分块
  private int blockCount = 10;
  // 用等号表示已完成的进度块
  private char finishedBlockChar = '=';
  // 用空格表示未完成的进度块
  private char unfinishedBlockChar = ' ';

  // 最大进度值
  private int max;

  ProgressBar(int max) {
    this.max = max;
  }

  // 更新进度
  void onProgressChanged(int progress) {
    // 工作完成后不执行任何进度信息更新
    if (done) {
      return;
    }

    // 初次出输进度时需要另起一行,避免覆盖之前输出的其它内容
    if (inited) {
      System.out.print("\n");
    }

    if (progress >= max) {
      progress = max;
      done = true;
    }
    System.out.print(createProgressContent(progress));

    inited = false;

    // 工作完成后需换行,以避免后续其它内容出现在进度条所在行
    if (done) {
      System.out.print("\n");
    }
  }

  private String createProgressContent(int progress) {
    StringBuilder sb = new StringBuilder("\rProgress: [");

    int finishedBlockCount = progress * blockCount / max;
    int unfinishedBlockCount = blockCount - finishedBlockCount;
    for (int i = 0; i < finishedBlockCount; i++) {
      sb.append(finishedBlockChar);
    }
    for (int i = 0; i < unfinishedBlockCount; i++) {
      sb.append(unfinishedBlockChar);
    }

    sb.append("] ");
    sb.append(progress);
    sb.append("/");
    sb.append(max);

    return sb.toString();
  }
}

 

使用示例

public static void main(String[] args) {

  // 限定最大进度值为100
  int max = 100;

  ProgressBar progressBar = new ProgressBar(max);
  // 由工作线程主动发起进度条更新
  Worker worker = new Worker(max, progressBar);
  worker.start();
}
  • 大小: 27.2 KB
  • 大小: 1.6 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics