[Linux] プログレスバーアプレットを作成する
C 言語の \n および \r 文字
C 言語には 2 種類の文字があります。
- 表示可能な文字
- 制御文字
表示可能な文字は文字aなどの文字、制御文字は\nなどの制御文字です。
プログレスバーを作成するには、次の 2 つの制御文字に注意するだけで済みます。
- \r – キャリッジリターン操作を実行します
- \n – 改行と復帰の操作を実行します
説明: \n 自体は改行文字ですが、C 言語自体はこれを改行とキャリッジ リターンに解析します。
文字 \r と \n の役割をよりよく理解するために、いくつかのテストを行う必要があります。コンパイルを容易にするために、メイクファイル ファイルを作成します。ファイルの内容は次のとおりです。
mytest:test.c
gcc -o mytest test.c
.PHONY:clean
clean:
rm -f mytest
まず次のコードを実行します。
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world");
sleep(3);
fflush(stdout);
return 0;
}
例証します:
- fflush(stdout) は、現象の観察を容易にするために標準出力バッファをリフレッシュします。
- sleep は Linux システムのスリープ機能です。
実行結果は以下の通りです。
を出力しhello world
た後、プログラムはスリープ状態になり、「カーソル」は同じ行の次の位置にあります。
休止状態終了後はカーソル位置から印刷が開始されるため、その直後にコマンド[qxm@aliyun-centos review]$
ラインプロンプトが印刷されますhello world
。
次に、次のコードを実行します。
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world\n");
sleep(3);
return 0;
}
実行結果は以下の通りです。
\n は C 言語によって改行とそれに続くキャリッジ リターンに解析されるため、プログラムは印刷hello world
後に 「カーソル」は次の行の先頭に移動します。
休止状態が終了すると、カーソル位置から印刷が開始されるため、[qxm@aliyun-centos review]$
コマンド ライン プロンプトは次の行の先頭に印刷されます。
最後に次のコードを実行します。
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world\n");
sleep(3);
return 0;
}
例証します:
- fflush(stdout) は、現象の観察を容易にするために標準出力バッファをリフレッシュします。
- sleep は Linux システムのスリープ機能です。
実行結果は以下の通りです。
\r はキャリッジリターンであるため、プログラムは印刷hello world
後に、「カーソル」は行の先頭に戻ります。
休止状態が終了すると、カーソル位置から印刷が開始されるため、コマンド ライン プロンプトは[qxm@aliyun-centos review]$
元のプロンプトhello world
を上書きします。
バッファリフレッシュ戦略
Linux システムでは、C 言語はまず印刷する文字をバッファに格納します。バッファ内の文字が画面に更新されたときにのみ、画面に表示されます。バッファの更新状況は次のとおりです。
- \n に遭遇すると、\n より前のすべての文字が画面にフラッシュされます。
- バッファはプログラムが終了すると自動的にフラッシュされます。
バッファテストでは、次のコードを実行します。
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world\r");
sleep(3);
return 0;
}
実行結果は以下の通りです。
バッファはフラッシュされないため、プログラムがスリープしている間は何も出力されません。
プログラム実行後、バッファは自動的に更新されてhello world
印刷されますが、\r キャリッジリターンは「カーソル」を行頭に戻すため、コマンドプロンプトの印刷は前回の印刷を上書きします。
次に、次のコードを実行します。
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world\n");
sleep(3);
return 0;
}
実行結果は以下の通りです。
遭遇のため\nバッファ内のデータはスリープ前にリフレッシュされました。
プログラムが休止状態になった後、コマンド ライン プロンプトはカーソル位置から印刷を開始します。
進行状況バーコードの書き込み
次のファイルを作成してコード構造を形成します。
- myproc.h – 進行状況バーコードを保持する宣言
- myproc.c – 保存進行状況バーコードの実装
- main.c – プログレスバーコードの呼び出し
myproc.h ファイルのコア構造は次のとおりです。
#pragma once
#include <stdio.h>
extern void process();
myproc.c ファイルのコア構造は次のとおりです。
#include "myproc.h"
void process()
{
//...
}
main.c ファイルのコア構造は次のとおりです。
#include "myproc.h"
int main()
{
process();
return 0;
}
同時に、makefile ファイルを作成し、makefile ファイル内に次の内容を書き込みます。
myproc:myproc.c main.c
gcc -o myproc myproc.c main.c
.PHONY:clean
clean:
rm -f myproc
コード構造を確立した後、グラフィカルな進行状況バーとして機能する次のコードを作成します。
#include "myproc.h"
#include <string.h>
#include <unistd.h>
#define STYLE '='
#define ARROW '>'
#define SIZE 101
void process()
{
char buf[SIZE];
memset(buf, 0 , SIZE);
int i = 0;
while(i <= 100)
{
printf("[%-100s]\r", buf);
fflush(stdout);
buf[i++] = STYLE;
if(i != 100 )buf[i] = ARROW;
usleep(100000);
}
printf("\n");
}
まず、印刷するグラフィカル プログレス バー buf を保存する文字列を作成し、初期化してから、印刷時に buf を左揃えで 100 文字の長さで印刷します。
印刷が完了するたびに、キャリッジ リターンによって前の印刷が上書きされ、バッファが更新されて印刷が画面に表示され、スリープ機能を使用して進行状況が読み込まれます。
印刷効果は次のとおりです。
グラフィカルな進行状況バーに加えて、デジタル進行状況表示も設定する必要があるため、印刷を次のように変更する必要があります。
printf("[%-100s][%d%%]\r", buf, i);
デジタル化の進行状況表示として印刷の進行状況のパーセンテージを増やします。%% は % にエスケープされ、画面に印刷されます。
印刷効果は次のとおりです。
最後に、進行状況バーが継続的に実行されていることを示す回転カーソルが追加されるため、印刷を変更する必要があります。最終的なコードは次のとおりです。
#include "myproc.h"
#include <string.h>
#include <unistd.h>
#define STYLE '='
#define ARROW '>'
#define SIZE 101
void process()
{
const char* cursor = "|/-\\";
char buf[SIZE];
memset(buf, 0 , SIZE);
int i = 0;
while(i <= 100)
{
printf("[%-100s][%d%%][%c]\r", buf, i, cursor[i%4]);
fflush(stdout);
buf[i++] = STYLE;
if(i != 100 )buf[i] = ARROW;
usleep(100000);
}
printf("\n");
}
回転カーソルのスタイルを保存するためにカーソル文字列が追加されます。回転カーソルでは 4 文字が循環的に出力されるため、\\ は \ にエスケープされ、スタイル文字列はモジュロ 4 で出力されます。
プログレスバーの最終的なデモンストレーション結果は次のとおりです。