久保田です。
今日は小ネタです。
エンジニアの方々は、おそらく毎日コマンドラインを使っていると思います。
そして、コマンドラインを使っているなら、このような表示をwgetやdockerなど様々なところで見かけると思います。
僕はエンジニアになりたての頃、このプログレスバーがどのように作られているかふわっと疑問に思っていました。
普通にコンソールにログを出したらこうはならないじゃないか、と
一行ずつ表示しちゃうじゃないか、と
でもある日 キャリッジリターン だと気がつきました。
通常改行時に\n
とセットで使う\r
、これを使います。
\r
だけで使用すると、カーソル行頭復帰するので、
出力 -> キャリッジリターン -> 出力 -> キャリッジリターン -> 出力 を繰り返せばよかったのです。
歴史的には、以下のような感じらしいです。(wikipediaより)
キャリッジ・リターン(carriage return)は本来、テレタイプ端末の Baudot Code における制御文字を指す用語で、行末から行頭に戻す復帰コードであって、改行コードを含まない。その後、タイプライターで一行打ち込んだ後で紙を固定するシリンダー(キャリッジ)を次の行の先頭にタイプできるように戻し(リターン)改行する機構(またはその機構を操作するレバー)を「キャリッジ・リターン」と呼んだ。
というわけで、goで実装しました。
今回は例なので、
- 100%になるまでランダムに計算を繰り返し、%に合うだけ=を出力 & キャリッジリターンする。
- 今回はランダムで数字を与え続け、30になるまで繰り返す。
という流れです。
package main import ( "fmt" "math/rand" "strings" "time" ) type ProgressBar struct { max int progress int percent float64 } func main() { // 乱数生成器作成 rand.Seed(time.Now().UnixNano()) // プログレスバーを表す構造体 progressBar := ProgressBar{max: 30, progress: 0} for { // 加算処理 randNum := rand.Intn(10) progressBar.progress += int(randNum) // max値を超えないようにする if progressBar.progress > progressBar.max { progressBar.progress -= (progressBar.progress - progressBar.max) } // プログレスのパーセンテージ progressBar.percent = (float64(progressBar.progress) / float64(progressBar.max)) * 100 // いくつ=を表示するかを決めるため、intに変換 roundPercent := int(progressBar.percent) // ===> のように >付きで表示したいので、-1する。 if roundPercent > 0 { roundPercent -= 1 } // .も合わせて最大5桁、小数点は1桁で出力。 fmt.Print(fmt.Sprintf("%5.1f%% [%s>%s] %d \r", progressBar.percent, strings.Repeat("=", roundPercent), strings.Repeat(" ", 99-roundPercent), progressBar.progress)) if progressBar.progress == progressBar.max { fmt.Println("") break } time.Sleep(1 * time.Second) } }
こんな感じです。
今回は簡単な例ですが、 このような出力の仕組みを利用すると、CUIで人を驚かせられるような面白い遊びができるので、ぜひ試してみてください。
(表示上は消えているように見せたり、動いているように見せたりできます。)