最近、割と16進数文字列で表現されたデータに遭遇することが多くなったので、
perlでうんぬんやるのもいいんだけど、やってることは、2文字取り出して数値へ変換しているだけなんで、
C言語でも簡単にかけそうだな、と思ったわけです。
調べれば有用なソフトはいくつでも見つかりそうですが、自分で書きたいから書きました。
以下そのコード。
コード
#include <stdio.h> #include <stdlib.h> #include <string.h> #define BUFFER_SIZE (4096) int main(int argc, char **argv){ int buffer_size = BUFFER_SIZE; int len, i, c; unsigned char sum; char *buffer; char *in_file = NULL; char *out_file = NULL; FILE *in = stdin; FILE *out = stdout; if(argc > 1){ if(memcmp(argv[1], "-h", 3) == 0){ fprintf(stderr, "usage: hex2bin [OPTION] FILE or hex2bin < IN [ > OUT]\n"); fprintf(stderr, "OPTION:\n" " -s [size] : buffer size. (default: %dbyte)\n" " -i [IN] : input file path. (if you use STDIN, do not use this option)\n" " -o [OUT] : output file path.\n", BUFFER_SIZE); fprintf(stderr, "IN : input file path.\n"); fprintf(stderr, "OUT: output file path.\n"); return 0; } // arg while(argc > 2){ if(memcmp(argv[1], "-s", 3) == 0){ buffer_size = atoi(argv[2]); argv+=2; argc--; }else if(memcmp(argv[1], "-o", 3) == 0){ out_file = argv[2]; if((out=fopen(out_file, "wb")) == NULL){ fprintf(stderr, "can't open file: %s\n", out_file); return -1; } argv+=2; argc--; }else if(memcmp(argv[1], "-i", 3)==0){ in_file = argv[2]; argv+=2; argc--; } argc--; } if(in_file == NULL && argc>1) in_file = argv[1]; if(in_file!=NULL){ if((in=fopen(in_file, "rb")) == NULL){ fprintf(stderr, "can't open file: %s\n", in_file); return -1; } } } if(buffer_size < 1) buffer_size = BUFFER_SIZE; if((buffer = (char *)malloc(buffer_size)) == NULL){ fprintf(stderr, "Buffer allocation failed : size=%d\n", buffer_size); return -1; } /* fprintf(stderr, "buffer size: %dbyte\n", buffer_size); if(in != stdin) fprintf(stderr, "input: %s\n", in_file); if(out != stdout) fprintf(stderr, "output: %s\n", out_file); */ c=0; sum=0; while(len = fread(buffer, 1, buffer_size, in)){ for(i=0; i<len; ++i){ if(buffer[i] >='0' && buffer[i] <= '9'){ sum *= 16; sum += buffer[i] -'0'; c++; }else if(buffer[i] >='A' && buffer[i] <= 'F'){ sum *= 16; sum += buffer[i] -'A'+10; c++; }else if(buffer[i] >='a' && buffer[i] <= 'f'){ sum *= 16; sum += buffer[i]-'a'+10; c++; } if(c==2){ fwrite(&sum, 1, 1, out); c=0; sum=0; } } } fclose(in); fclose(out); return 0; }
このプログラムには見ての通り欠点がいくつかあります。最後のほうに書きます。
本体はこちら。自己責任でどうぞ。
http://www.rying.net/arc/hex2bin.exe
つかいかた
hexdata.txtに16進数字のみで書いたとして、
hex2bin < hexdata.txt とやると、標準出力にバイナリデータが得られます。
hex2bin < hexdata.txt > a.bin とやると、a.binに書き出されます。
hex2bin -h で簡単なコマンドラインの説明っぽいのがでます。
また、オプションに、-s, -i, -oを用意しています。
-s: バッファサイズを指定。デフォルトで4096あるので、あまりいじる必要はないです。
-i: 入力ファイルを指定。標準入力を使う場合は指定しないこと。
-o: 出力ファイルを指定。
hex2bin < hexdata.txt > a.bin
hex2bin -i hexdata.txt -o a.bin
hex2bin -o a.bin hexdata.txt
これらは同じ処理を行うはずです。
欠点
このプログラムはファイルの先頭から読み取っていき、
16進数文字を2個得られたら出力しているだけです。なので、それ以外の文字は無視します。
これに適合しないデータはフォーマットが悪いということにします。
つまり、入力に適したデータは、
16進数文字のみで構成されており、適度に空白や改行で分かりやすくなっているもの
に限られます。
主な欠点
1x2-3 45 とデータが書かれていた場合、 0x12 0x34が出力される。
0x00などには対応しない。(0xの0の部分も16進数文字と解釈して読み取ってしまう。)
\x00は大丈夫です。(\は16進数文字でないので)
他に問題がありそうです。特に引数周りの処理は初めてやってみたのでどうなることやら。
0xの対策
0の次にxが出てきたら、カウンターをリセットすればよさそうです。
}else if(buffer[i] >='a' && buffer[i] <= 'f'){ sum *= 16; sum += buffer[i]-'a'+10; c++; }
のあとにでも、
else if(buffer[i] >='x'){ c=0; sum=0; }
とか。
試してないんで大丈夫かわからんけども。