🔖

perl unpack(h)を他言語で再現するのは面倒だった

2023/03/20に公開

wiki engineでは、日本語タイトルをそのまま扱うために、なんらかの方法でASCII文字に変換することはよくある。

例えば次の通り。

https://github.com/hymkor/wifky/blob/master/wifky.pl#L720-L727

とある事情で、タイトルだけ別のプログラムで利用しようと思ったのだが、ここではまった。
python3 bytes.formhex()で済むだろうと思い込んでた。ChatGPTもそういってた
しかしエンコードしたはずの文字列は期待したように読めなかったのだ。

そこまででようやく気づいたのは unpack(h) は小生が現在愛用するpython3には存在しないのだ。

https://perldoc.jp/func/pack

    h  16 進数文字列 (低位ニブルが先)。
    H  16 進数文字列 (高位ニブルが先)。

ニブルとは4ビットのことで、16進数でいえばただの1桁にあたる。つまりb'1B'は、"B1"に変換されてることを意味する。

https://docs.python.org/ja/3/library/struct.html#byte-order-size-and-alignment

一方、pythonではバイトオーダーの話しかしてない。16進数でいえば2桁である。

どうやら2バイト分それぞれ、上下ニブルを入れ替えなければならないらしい。

しかたないので書いた。

def perlunpackh(hex_str: str) -> str:
    nowstr = bytearray()
    for ii in range(0, len(hex_str), 2):
        nib = hex_str[ii:ii+2]
        rnib = nib[-1] + nib[0]
        byte_val = int(rnib,16)
        nowstr.append(byte_val)
    nowstr = nowstr.decode("utf-8")
    return nowstr

内包表記では書き直せるが、これ以上短くしてもあまり意味がなかったので妥協した。

Discussion