お仕事で、たまにpythonを触ることがある。
大したことをするわけではなく、ライブラリと一緒に配布されているexamplesに、ちょっと手を加えて実行する程度で、本格的にアプリを組むというのはやったことがない。
自覚しているが、私はどうもスクリプト言語が苦手だ。
そのせいか、pythonも便利そうなことはわかるのだけど、本気で覚える気力がわいていない。
これから使う機会が多いので、イヤイヤ期は卒業せねばならぬのだが。。。
さて、pythonがどうだ、というわけではないのだけど、16進数の扱いが難しいと感じる。
私がやると、どうしてもプロトコル解析とか、バイナリデータ操作とかが主なので、16進数で1バイトずつアクセスしたいのだ。
先週、ようやく
foo = '123456789abcdef'.decode('hex')
print foo.encode('hex')
という形でそれっぽく扱うことができることがわかった。
これでもう怖くない!と思っていたのだが、昨日使ったexamplesでは「decode()なんてないよ」といきなり怒られてしまった。
しばらく気付かなかったのだが、ファイルの先頭に「python3」などと書かれている。
gcc3とかgcc4とかと同じようなものかと思ったが、そうではなく、言語のバージョン自体が違うらしい。
そういえば、python2系とpython3系でどうのこうの、という記述を見たことがある。。。
どうも、python3では「bytes」という型があるらしい。
python3のbytes型とstr型の比較と変換方法 | Python Snippets
文字列の前に「b」と付けると、bytes型になるようだ。
16進数をバイト列にすると、こういう感じでよさそうだ。
foo = bytes.fromhex('123456789abcdef0')
printすると「\x」がついて邪魔なのだが、文字列じゃないからそのまま出力されても困るし、仕方ないところか。
変換すればよいだけなので、よしとしよう。
ただ、bytes型だけじゃなく、整数型としても16進数を扱えるので、「あれ、自分は16進数のデータをどうやって扱っているんだっけ?」ということになってしまう。
ライブラリに頼っているので、結局は受け入れてくれる形に変換するしかないのだ。
朝の続き。
bytesの16進数を、文字列で表示させたい。
C言語で言えば、こういう感じだ。
const uint8_t foo[] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 };
for (int lp = 0; lp < sizeof(foo); lp++) {
printf("%02x", foo[lp]);
}
printf("\n");
python3だと、こうかな?
foo = bytes.fromhex('123456789abcdef0')
import binascii
print(binascii.hexlify(foo))
hexlify()は、b2a_hex()でもよいらしい。
これは悪くないのだが、
b'123456789abcdef0'
と、「b」とかシングルクオーテーションが付いてしまうのが気にくわない。
文字列だろうから、と「[2:]」などとしても、b'は消えない。
これは、hexilify()の戻り値がbytesだからだ。
つまり、16進数文字列を、16進数としてbytesに変換するだけだから、print()の方がb'を付けていることになる。
ちなみに、
foo = b'123456789abcdef0'
でやると、
b'31323334353637383961626364656630'
となる。
つまり、文字列を16進数にしているのだ。
C言語で言えば、文字列中にエスケープシーケンス\xで16進数を直接書いた場合に使うことになろう。
数値に変換したい場合は、C言語だとこう書くかな。
long val = strtol("abcdef", NULL, 16);
確か、文字列が0xで始まっていても、うまいことやってくれたような気がする。
print(int('123456789abcdef0', 16))
でやると、
1311768467463790320
となった。
「1311768467463790320 = 0x123456789ABCDEF0」だから、そのままの見た目で変換してくれるようだ。
であれば、さっきの話に戻るが、
print('%x' % int('123456789abcdef0', 16))
とすると、
123456789abcdef0
となるので、まだるっこしいが「b」やらシングルクオーテーションやらは取り除かれる。
目的は達するけど、ちょっとねぇ。。。。
こんなことすると、
print(('%x' % int('123456789abcdef0', 16)).zfill(20))
結果は、
0000123456789abcdef0
とゼロ埋めしてくれるので、悪くない。。。のかな。。。
ただ、数値で扱える範囲を超してしまうとダメだろうから、やはり1要素ずつ変換するのが素直なのではなかろうか。
foo = bytes.fromhex('123456789abcdef0')
print(''.join(format(x, '02x') for x in foo))
だと、
123456789abcdef0
なので、私が思っているイメージになった。
0 件のコメント:
コメントを投稿
コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。
注: コメントを投稿できるのは、このブログのメンバーだけです。