時間がない方に向けて結論
ハッシュ文字列をzip圧縮すると、長くなります。
SHA256の場合:元256bit=64文字=32byte → 137byte(428%増)
SHA3_512の場合:元512bit=128文字=64byte → 169byte(264%増)
当たり前だろという声も聞こえる気がします。
恥を忍んでソースはこちら
https://github.com/yo16/compress_str
はじめに思ったこと
ハッシュ化して256bitなり512bitなりになった後の16進数の文字列を見ると、異常に長く感じますよね?長いですわ。例えばSHA256でも661b976821ad4a7545054a2e45367e3af53522477d39b28fdca26b36fed95f8b1a2005e3188b682a74f9e772aa3cb7201fcb6d01ce6cb2cdf720690fd26d5bb1e
になって64文字。512だと倍。これをDBへ格納して、なんなら表示したりとか気が知れない。
だけどそもそも16進数、つまり読み直せば2進数、バイナリですよね。上のも、011110000101001010111000010101011....(さすがに略.16進数の4倍
みたいに0/1の値。こうやって表現すると、これってコンピューターの中のバイナリファイルだなーと思いました。
そしたらそのバイナリファイルを圧縮すれば小さくなるんだから、圧縮結果をもう一度2進数にして、16進数にすれば、短い文字列ができるんじゃないか・・・?
実験
1. 文字列をハッシュ化する関数
記事の本筋ではないので超絶割愛。知りたければGitHubのソースを見てください。
# キモのところ
import hashlib
h = hashlib.sha256(s).hexdigest()
2. zip圧縮
ハッシュ化した文字列をバイナリ化して、zip化して、文字列化(16進数)する。
難しいことはないのだけれど、普段、バイナリの操作はあまりやらないので試行錯誤。
def do_compress(str_hex:str)->str:
"""文字列の16進数をバイナリ値にそのまま変換し、zip圧縮して返す
Args:
str_hex (str): 16進数の文字列(先頭にbはない)
Returns:
str: zip圧縮したバイナリ値を16進数にした文字列
"""
# 16進数文字列をそのままバイナリ化
b = bytes.fromhex(str_hex)
# バイナリをzip化
zip_stream = io.BytesIO()
with zipfile.ZipFile(zip_stream, 'w', compression=zipfile.ZIP_DEFLATED) as new_zip:
new_zip.writestr('_', b)
# zipしたバイナリ値をそのまま取得
h = zip_stream.getvalue()
# 16進数にして返す
return h.hex()
3. 評価
実行するところとその結果。
test_str = 'test123'
#ret = do_hashing(test_str, ret_type='Hex', hash_method='SHA256')
ret = do_hashing(test_str, ret_type='Hex', hash_method='SHA3_512')
print(ret)
print(f'{len(ret)/2} bytes') # 16進数1文字=4bit=0.5byte
# >>> 64.0 bytes
ret2 = do_compress(ret)
print(ret2)
print(f'{len(ret2)/2} bytes')
# >>> 169.0 bytes
最初に書いた通り、
- SHA256の場合
- 元256bit = 64文字 = 32byte
- → Zip → 137byte(428%増)
- SHA3_512の場合
- 元512bit = 128文字 = 64byte
- → Zip → 169byte(264%増)
ZIP圧縮方法変更してもっと圧縮するとかは、もうそういうレベルではないのでやってません。
あとがき
残念でしたが、思いついたことがダメだとすぐ判明してスッキリしました。コーディングできる人でよかった。(間違ってなければ)