コンソール画面の制御(C++)

エスケープシーケンス
第47回特殊な画面制御~コンソール入出力関数とエスケープシーケンス
右詰め/右詰め/ゼロ埋めの方法

プログレスバー

ここでのポイントは、setfill()setw()で任意文字で桁数だけ埋める処理と、キャリッジリターン\rを出力して、標準出力のカーソルを行頭に移動することによって、画面スクロールせずにプログレスバーを表現させることができる。コマンドライン処理ではよく見る処理になる。

あと、\e[1;33;47m(ボールド、黄色、背景白色)と\e[0mで囲むことによって色が付与できる。最後は必ず\e[0mをつけて「色の付与はここで終了」とさせないと以降の出力すべてに色がついてしまう。色のルールは上記サイトを参考にする。

1行だけで表現するときに有効な方法になる。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <iostream>
#include <iomanip>

using namespace std;

int main(int argc, char*argv[]){
  // スクリーンをクリア
  system("clear");

  int count = 0;
  string progress = "";
  int percent = 0;
  while(1){
    
    percent = count / 10;
    if(count % 100 == 0 && progress.size()< 10){
      progress += "#";
    }
    if( percent > 100 ){
      percent = 100;
    }
    
    // 半角スペースを10個、左詰めで'#'を上書きする
    // さらにスペースを7個、右詰めでパーセンテージを表示
    cout << "[" << "\e[1;33;47m" << setfill(' ') << setw(10) << left << progress << "\e[0m"<<"]"
      << setfill(' ') << setw(7) << right << percent << "%";

    // 100%まで行ったら改行を入れてループを抜ける
    if( progress.size() == 10 && percent == 100 ){
      cout<<endl;
      break;
    }
    
    // キャリッジリターン(行頭にカーソルを移動する)
    cout<<"\r";

    count++;
    usleep(10000);
  }

  return 0;
}

実行結果。バーと数値が若干ずれるが気にしない。

> g++ sample.cpp && ./a.out
[#####     ]     43%
[##########]    100%
>

複数行

キャリッジリターンでは対応できない。\e[nAでカーソルをn行上に移動するエスケープを使って表現する。あと、カーソルがちらつく場合、\e[?25l(カーソル非表示)と\e[?25h(カーソル表示)を使ったほうが良さそうだった。

終了時にカーソルを4行下まで移動(\e[4B)した後でプログラムが終了してプロンプトが表示される。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <iostream>
#include <iomanip>

using namespace std;

int main(int argc, char*argv[]){
    // カーソルを非表示にする
  cout<<"\e[?25l";
  // スクリーンをクリア
  system("clear");
  
  int count = 0;
  int count2 = 0;
  while(1){
    cout<<"-----\n";
    cout<< setfill(' ') << setw(5) << right << count<< "|" << setfill(' ') << setw(3) << count/20<<"\n";
    cout<<"-----\n";
    cout<< setfill('#') << setw(count/50) <<"\n";
    cout<<"-----\n";
    cout<<flush;

    // 5行上に移動
    cout<<"\e[5A";
    
    if( count > 1000 ){
      break;
    }
    count++;
    usleep(10000);
  }
  
  cout<<endl;

  // 4行下にカーソル移動する
  cout<<"\e[4B";

  // カーソルを表示する
  cout<<"\e[?25h";
  
  return 0;
}

実行結果

複数行で動きのある標準出力になる。

> g++ sample.cpp && ./a.out
(一旦画面がクリアされる)
-----
  254| 12
-----
####
-----
-----
 1001| 50
-----
###################
-----
>