web-dev-qa-db-ja.com

再印刷せずに端末で印刷されたメッセージを更新する方法

次のように機能するターミナルアプリケーションのプログレスバーを作成したいと思います。

 [XXXXXXX       ] 

これにより、プロセスが完了するまでに残っている時間が視覚的に示されます。

Xを文字列に追加してから単にprintfすることで、Xをどんどん印刷するようなことができることはわかっていますが、次のようになります。

 [XXXXXXX       ] 
 [XXXXXXXX      ] 
 [XXXXXXXXX     ] 
 [XXXXXXXXXX    ] 

またはそのようなもの(明らかに間隔で遊ぶことができます。)しかし、これは視覚的に美的ではありません。ターミナルで印刷されたテキストを、再印刷せずに新しいテキストで更新する方法はありますか?これはすべてLinux、C++の下にあります。

41
ldog

新しい「バージョン」を印刷するときは、\rの代わりに\nを使用してみてください。

for(int i=0;i<=100;++i) printf("\r[%3d%%]",i);
printf("\n");

Ncursesのようなライブラリはそのようなことに慣れていると思います。 cursesは、カーソルを画面上で動かしたり、テキストなどを描画したりするのに役立ちます。

NCurses

11
KFro

このようなもの:

std::stringstream out;
for (int i = 0; i< 10; i++)
{
  out << "X";
  cout << "\r" << "[" << out.str() << "]";
}

卑劣なビットはキャリッジリターン文字「\ r」で、カーソルを次の行に移動せずに行の先頭に移動させます。

7

\rを使用して現在の行の先頭に戻り、行全体を上書きできることをすでに指摘している人もいます。

もう1つの可能性は、バックスペース文字( "\ b")を使用していくつかのスペースを消去し、それらのスペースのみを上書きすることです。これにはいくつかの利点があります。まず、ライン内のすべてを再生成する必要がないことは明らかです。これは、軽度の痛みを伴う場合があります(ただし、これはかなり珍しいことです)。第2に、データを書き込むときにサイズが縮小するデータを表示する際の問題を回避できます。たとえば、\rを使用して100から0までのカウントダウンを表示する場合、前の長さ全体を上書きすることに注意してください。そうしないと、カウントダウンが(たとえば)100から990になります(つまり、前の「0」はそのままになります)。

ただし、行内のバックスペースは通常は機能しますが、行の先頭のバックスペースはカーソル/書き込み位置を前の行に戻す場合と戻さない場合があることに注意してください。ほとんどの実用的な目的では、1行内でのみ移動できます。

3
Jerry Coffin

もう1つのオプションは、一度に1文字を印刷することです。通常、stdoutはラインバッファリングされているため、fflush(stdout)-を呼び出す必要があります。

for(int i = 0; i < 50; ++i) {
   putchar('X'); fflush(stdout);
   /* do some stuff here */
}
putchar('\n');

しかし、これには完了を示すニースの終了「]」がありません。

1
NVRAM

'\ r'はキャリッジリターンを実行します。改行なしでキャリッジリターンを実行するプリンタを想像してみてください( '\ n')。これにより、書き込みポイントが行の先頭に戻ります...次に、更新されたステータスを元の行の上に再印刷します。

1
kjfletch

それは別の言語ですが、 この質問 はあなたの助けになるかもしれません。基本的に、エスケープ文字\ r(\ n改行ではなくキャリッジリターン)を使用すると、現在の印刷行の先頭に戻るため、すでに印刷したものを上書きできます。

1
Tim

このローディングバーユーティリティは、少し前に作成しました。役に立つかもしれません...

https://github.com/BlaDrzz/CppUtility/tree/master/LoadingBar

ここでは基本的に何でもカスタマイズできます。

int max = 1000;
LoadingBar* lb = new LoadingBar(10, 0, max);

for (size_t i = 0; i <= max; i++)
{
    lb->print();
    lb->iterate();
}
cout << lb->toString() << endl;

非常にシンプルでカスタマイズ可能な実装。

0
Jannes D.