人生100年!生涯エンジニア人生!

楽しいエンジニア人生!

CalorieMate to Programmer #うちこむ人にバランス栄養 msg19 をLinuxのコマンドでTranslateする

概要

『CalorieMate to Programmer #うちこむ人にバランス栄養』というプロモーションがありました。   www.otsuka.co.jp

その中には色々なプログラム言語でのメッセージがあります。

www.otsuka.co.jp

msg19のメッセージが、ちょっと変わっていてバイナリーコードです。
最初はプログラムコードかな?と思ったのですが、トップページから言語メッセージを表示すると画面下の「Translate」が出てきます。
その「Translate」を押下すると、BALANCED FOR HUMAN.と出ます。
バイナリーコードの正体は文字コードでした。

でも、エンジニアなので自分で作ったコードで、バイナリーコードをTranslateしたいですよね!!

手順

mac OSまたはWindows Subsystem for Linuxで動かすことを想定してます。

www.otsuka.co.jp

CUIモードに入って、バイナリーコードを取得します。
cd messagesと打ってからcat msg19.mdと打てばデータが取れます。
以下のようなデータが取れます。

 01000010 01000001 01001100
 01000001 01001110 01000011
 01000101 01000100 00100000
 01000110 01001111 01010010
 00100000 01001000 01010101
 01001101 01000001 01001110
 00101110

これをローカルマシンにコピーしてファイル化します。
そして、以下のコマンドを打ちます。

cat msg19.md | for n in `xargs`;do echo "obase=16;ibase=2;$n" | bc | xxd -r -p ;done;echo

BALANCED FOR HUMAN.と表示されました!

解説

やっていることは、こんな感じです。

  1. xargsで改行とスペースで区切られた標準入力を読み込み、スペースで区切られた1行の文字列に変換する。
  2. それをforに送り込むことで配列となり、ループするときに変数nに配列から抽出された文字列が入る。
  3. echoでbcコマンドに必要な文字列を追加する。
  4. bcコマンドで、2進数から16進数に変換する。
  5. xxdコマンドで16進数から文字列に変換する。

xargsでスペースで区切られた1行の文字列に変換しています。
forを使うことでスペース区切りは配列と認識され、nという変数にはループするごとに順番に配列から取得されていきます。
この仕組みを使うことで、パイプでつなげたbcコマンドやxxdコマンドを実行できるようにしています。

bcコマンドは色々なことができるのですが、今回はibase=2;を指定して2進数から、obase=16;を指定して16進数変換に使用しました。(順番は変えないでください)
xxdコマンドで文字列に変換すると書きましたが、このコマンドはデータを数値にダンプするもので、それをオプションの-rで逆変換しています。 (-pはプレーンフォーマットであることを明示するオプションです)
※xxdコマンドは16進数から逆変換できるのですが、2進数から逆変換ができないので、bcコマンドで2進数を16進数に変換します。

カロリーメイトCUIで遊んだら、本物のCUIでも遊んでね💖

Perl

cat msg19.md | for n in `xargs`;do echo "$n" | perl -e 'print pack("B*",<>)';done;echo

bcコマンドとxxdコマンドをPerlのpackに置き換えます。
バイナリーコードを文字に変換するのでpackを使います、2進数のバイナリーコードなので"B*"を指定します。
標準入力は<>で取得してます。

書いてて思いましたが、Perlのオプションを使うと、もう少し短くなりますし、Shellのforが不要になります。
やってみました、こんな感じです。

cat msg19.md | perl -ane 'foreach(@F){print pack("B*",$_)}END{print"\n"}'

-nでwhileモード、-aでオートスプリットモードで、@Fには配列が入っているので、それをforeachで取り出し$_をpackして表示するだけです。

Ruby

cat msg19.md | ruby -ane '$F.each{|value|print [value].pack("B*")};END{puts}';

Perl版と発想は同じです。
Rubyは$Fに配列が入ります。

PHP

cat msg19.md | php -R 'foreach(explode(" ",trim($argn))as$value){echo pack("H*",base_convert($value,2,16));}' -E 'echo"\n";'

PHP5でも動きます。

PHPはオートスプリットモードが無いので、その部分は実装しないといけないので、標準入力を$argnで受け取り、explodeを使って配列にしていきます。
packは2進数変換が無いので、代わりに16進数に変換するためbase_convertを使用します。
行頭のスペースが配列に入ってしまうので、trimします。

Vue.js版

こちらは、今までとは逆に文字列をバイナリーコードに変換します。
実際に動いているのは、こちらになります。
msg19.netlify.app

ソースはこちらです。
github.com

bootstrap5 とかも使ってます。

Python

cat msg19.md | python -c 'import sys;import struct;[([(print(struct.pack("B",int(value,2)).decode(),end=""))for value in list])for list in[line.split()for line in sys.stdin]];print()'

Python3で動きます。

PythonPHPと同じでオートスプリットモードが無いので、その部分は実装しないといけないので、事前にimport sysをしておいてから、標準入力をsys.stdinで受け取り、splitを使ってlistにしていきます、そのlistを更にforで回していきます。
文字列をintの第2引数で2進数を指定して数値化してからpackにかけます。
packは事前にimport structしてから実行します。
packの実行結果はバイナリーデータのままなので、decodeして文字列化します。
printは、そのままだと改行されてしまうのでend=""で改行を抑止します。

Python版は物凄くややこしくなっています。

CUIモード

おまけ CUIモードが楽しかったです。
www.otsuka.co.jp 暇があったら、やってみましょう!

view-sourceにも遊び心がありました。
Developer toolのconsoleにもメッセージがありました。
以下ネタバレなので、コマンド探しをしたい人は見ないでください。
なお、helpコマンドと、helpコマンで紹介されているコマンドは記載してません。

ツイッターのモーメントにまとめました。

twitter.com