挨拶
はじめまして。たろうくん@卒論限界大学生と申します.
卒論が佳境ですが,この記事を書いています.
執筆と実装の日々に疲れ,何かクリエイティブなことをして癒されたくなったのです.
癒されたいときには,そう.
あのキャラクターが,頭に浮かぶんです...
(※この記事は、FUN Advent Calendar 2022 part2の17日目に書かせていただきました.前日の記事は以下のようになっています.)
(※著者は技術的に未熟なので変な部分があれば指摘していただければ幸いです)
はじめに
この記事を読んでいるのはコードを書いた経験がある方が多いと思います.
共感していただけるかと思いますが,開発に疲れてくるとイライラしてターミナル上でタイピングミスが増えたりしますよね.
そんな時には,癒しが欲しくなります.
そこで僕は爆発的な人気を集めているあの癒しキャラに,開発中にも会いたいと考えました.
(引用: アニメ「ちいかわ」公式サイト)
そう!ちいかわです!!
このキャラクターはイラストレーターのナガノさんによって生み出されたキャラで,その愛くるしさから熱狂的なファンを集めています.
私は開発が苦しいとき,ちいかわに癒されるためにちいかわコマンドの開発を決意しました.
この記事は開発したちいかわコマンドの概要,工夫点,実装内容を紹介するものです.
ここからは次の順番でちいかわコマンドについて解説していきます.
- ちいかわcsvファイルの生成
- csvファイルの読み込みと表示
- ちいかわプロセスを殺させない,殺してほしくない
- 機能の発展について
- とれなかったバグ
ちいかわcsvファイルの生成
ちいかわコマンドを作るにあたり,ターミナル上でちいかわを表現する必要があります.
ターミナル上で画像データなどの複雑なデータは表示しづらいですが,白と黒のドット絵のような形でなら表現できます.
白いスペースと黒いスペース文字を表示することで,画面にドット絵のちいかわを表現できるはずです.
そこで,今回はちいかわ画像をcsvファイル形式に変換し用いました.
まずいくつかのちいかわの画像を用意し,白と黒の二色画像に変換しました.
変換はドット絵こんばーたさんで,「色の数」に2を指定して行いました.
白黒の二値に変換した画像からドット絵ナニカさんでxlsxファイルを生成しました.
生成されたxlsxファイルを,csvファイルとしてexcelで新規保存しました.
このようにしてちいかわのドット絵を0と1で表したcsvデータを生成できました.
生成したデータは次のようになります.
csvファイルの読み込みと表示
次にcsvファイルの読み込みとドット絵の表示を行うプログラムを書きます.
プログラムの開発はgolangで行いました.
csvの読み込み
まず先ほど作ったcsvファイルを読み込みます.
読み込み部分のソースコードは次のようになっています.
/* ファイルオープン&エラーハンドリング */
file, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
defer file.Close()
/* encoding/csvパッケージによるファイル構造体のRead */
reader := csv.NewReader(file)
// rowsには[][]stringが返ってくる
rows, err := reader.ReadAll()
if err != nil {
log.Fatal(err)
}
for _, v := range rows {
// 一行ごとの処理
}
ドット絵の表示
次にドット絵部分の表示を行います.
読み込んだcsvファイルの一行ごとに,値が1の時は黒を表示,0の時は白を表示するという処理をします.
fmt.Printf関数でフォーマットの指定を行うことで文字の背景色を指定できます.
今回は背景色が,黒と白の半角スペースを用いることで白と黒のドット絵を表現します.
今回は表示するドット絵が大きいので,time.Sleepを用いて一行毎に表示して,ちいかわがゆっくりと描かれていく形にして全体像を見せたいと思います.
前項で書いた,一行ごとの処理はこのようになります.
for _, v := range rows {
for i := 0; i < len(v); i++ {
if v[i] == "0" {
// black
fmt.Printf("\x1b[40m%s\x1b[0m", " ")
}
if v[i] == "1" {
// white
fmt.Printf("\x1b[47m%s\x1b[0m", " ")
}
}
fmt.Printf("\n")
time.Sleep(time.Millisecond * 50)
}
この時点でちいかわの表示はできました.
実行すると以下のようになります.
ちいかわプロセスを殺させない,殺してほしくない
このようにして誕生したちいかわコマンドですが,Ctrl + C などで中断されてしまうとちいかわが切断されてしまいます...
そんなことはあってほしくない.
Ctrl + C やCtrl + Zを実行中に押すと,シグナルと呼ばれるものがターミナルで実行中のプログラムに送られ実行が中断されます.
例えばCtrl + Cが実行中に押下されると,プログラムにSIGINTが送られ,SIFINTを受けた際のデフォルト動作としてプログラムが中断します.
したがって,SIGINTなどのシグナルを受け取っても,何もしないというコードを書いてあげればこのように中断されることはありません.
シグナルを受けとっても何もしないコードを実装します.
goroutineでシグナルを受け取るプログラムを並列実行することで,いつシグナルが来てもプログラムが中断されなくできます.
プログラムを中断するシグナルであるSIGTERM, SIGQUIT, SIGINT, SIGTSTPを受け取って何もしないことでプログラムが中断しません.
コードは以下のようになります.
go func() {
trap := make(chan os.Signal, 1)
signal.Notify(trap, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTSTP)
}()
機能の発展について
この節では時間が足りず実装できなかった部分について,書き留めておきます.
来年,リベンジしたいです.
実行ファイルを一つにしたい
現在,実行ファイルの他にcsvファイルなどのアセットを用意しないといけないのですが,本当は実行ファイル内部にアセットを埋め込みたかったです.
実行ファイルを一つにできたほうが実行しやすいのですが,時間の関係で断念となりました.
go:embedなどを使用することで実現できるようです.
今後の展望としておきます.
とれなかったバグ
表示するドット絵の一番最初の列の一番右の値が表示されない.
原因不明でタイムアップとなりました.encoding/csv側の問題かと推測しています.
なんでなんだ!悔しい.
キーボードからCtrl + Cを入力したときターミナル上に^Cが表示されてしまう
今回の実装で実行中にCtrl + C を押しても中断されないようになるのですが,ターミナル上に^Cなどの文字が出力されてしまいます.
プログラムの実行中はキーボード入力を受け付けなくする必要があるようですが,対応策がわかりませんでした.
誰か教えてほしいです(´;ω;`)
終わりに
今回はちいかわコマンドを作りました.
記事を書いたのは初めての経験でしたが,とても楽しく,いい経験ができました.
FUN Advent Calendar 2022 part2,明日の担当は以下のようになっています.
- Sumeshi
- pman0214
- あかしやき
明日は私のボスも参加するので期待大ですね!
是非そちらもチェックしてみてください.
それでは今回はここでおひらきとなります.
お付き合いいただきありがとうございましたノシ