16進テキストをバイナリーに変換する
"hexdump -C" といったコマンドでバイナリーファイルを 16 進数テキストに変換することはできるのに、その逆はないよねえという話になった。そしてたしかに知らない。あれば便利なのに…。
ということでみんな大好き再発明のお時間です。:
// hex2bin.cc #include <algorithm> // std::find #include <iterator> // std::distance #include <stdexcept> // std::runtime_error, std::exception #include <cstdio> // putchar, getchar #include <iostream> // std::cerr template<typename T, size_t N> inline T* end_(T (&a)[N]) { return a + N; } // input single charecter from stdin, discarding white-space. inline int ch() { int c; do { c = getchar(); } while (9 == c || 10 == c || 12 == c || 32 == c); return c; } // convert hex charecter to int value 0..15 inline int from_(int h) { const char hex[] = "0123456789ABCDEFabcdef"; const size_t d = std::distance(hex, std::find(hex, end_(hex), h)); if (!(d < 22)) throw std::runtime_error("invalid hex charecter"); return d < 16 ? d : d - 6; } // get value 0..255 from pair of character inline int value() { const int i1 = ch(), i2 = ch(); if (EOF == i1) return EOF; if (EOF != i2) return 16 * from_(i1) + from_(i2); throw std::runtime_error("missing last hex character"); } int main() { try { int i; while ((i = value(), EOF != i)) putchar(i); } catch (const std::exception &e) { std::cerr << "ERROR: " << e.what() << std::endl; } return 0; }
こいつを次のようにビルドして…:
$ make hex2bin CXXFLAGS="-O2 -Wall"
動作確認。:
$ hexdump -e '16/1 "%02x " "\n"' hex2bin.cc | ./hex2bin // hex2bin.cc #include <algorithm> // std::find #include <iterator> // std::distance #include <stdexcept> // std::runtime_error, std::exception #include <cstdio> // putchar, getchar #include <iostream> // std::cerr template<typename T, size_t N> inline T* end_(T (&a)[N]) { return a + N; } // input single charecter from stdin, discarding white-space. inline int ch() { int c; do { c = getchar(); } while (9 == c || 10 == c || 12 == c || 32 == c); return c; } // convert hex charecter to int value 0..15 inline int from_(int h) { const char hex[] = "0123456789ABCDEFabcdef"; const size_t d = std::distance(hex, std::find(hex, end_(hex), h)); if (!(d < 22)) throw std::runtime_error("invalid hex charecter"); return d < 16 ? d : d - 6; } // get value 0..255 from pair of character inline int value() { const int i1 = ch(), i2 = ch(); if (EOF == i1) return EOF; if (EOF != i2) return 16 * from_(i1) + from_(i2); throw std::runtime_error("missing last hex character"); } int main() { try { int i; while ((i = value(), EOF != i)) putchar(i); } catch (const std::exception &e) { std::cerr << "ERROR: " << e.what() << std::endl; } return 0; }
hexdump の書式文字列の指定方法が、ちょっと難しいよなー…。覚えられると便利そうなんだけど。
与えているのは '16/1 "%02x " "\n"' という書式で、
- 16/1 は、続くパターンを 1 バイトごと 16 回繰り返す指定
- "%02x" で 16 進数を二桁で表示、一桁になる場合は 0 で埋める指定
- "\n" は改行出力指定
つまり 16 回、 1 バイトを %02x の書式で出力して区切りの改行を入れる、というパターンを実行している。