MD5ハッシュ値をURLセーフな文字列に変換

投稿者: | 2015年1月25日

大量に保存した画像ファイルなどの重複潰しも兼ねて、ファイル名を MD5 ハッシュ値にリネームして保存しています。

dyama/hashmv · GitHub

まったく同一のファイルであれば、ハッシュ値が同一になりますので、ファイル名が同一になります。上のスクリプト「hashmv」ではリネーム時に同一ファイルを上書きしますので、結果、重複ファイルを除去することができます。

実行イメージ

    $ hashmv foobar.jpg
    foobar.jpg -> ./d41d8cd98f00b204e9800998ecf8427e.jpg    

hashmv では、32 文字のハッシュ値のそのままファイル名に変換しているので、ちょっと長いなと思いました。md5sum のオプションを見ても、もうちょっとコンパクトな出力を得るオプションが見当たらなかったので、車輪の再発明上等でシェルスクリプトを書いてみました。

    #!bash
    function compress_hash()
    {
      echo $2 | sed \
        -e 's/aaa*/A/g' \
        -e 's/abb*/B/g' \
        -e 's/acc*/C/g' \
        -e 's/add*/D/g' \
        -e 's/aee*/E/g' \
        -e 's/aff*/F/g' \
        -e 's/baa*/G/g' -e 's/ecc*/g/g' \
        -e 's/bbb*/H/g' -e 's/edd*/h/g' \
        -e 's/bcc*/I/g' -e 's/eee*/i/g' \
        -e 's/bdd*/J/g' -e 's/eff*/j/g' \
        -e 's/bee*/K/g' -e 's/faa*/k/g' \
        -e 's/bff*/L/g' -e 's/fbb*/l/g' \
        -e 's/caa*/N/g' -e 's/fcc*/n/g' \
        -e 's/cbb*/M/g' -e 's/fdd*/m/g' \
        -e 's/ccc*/O/g' -e 's/fee*/o/g' \
        -e 's/cdd*/P/g' -e 's/fff*/p/g' \
        -e 's/cee*/Q/g' -e 's/0[0-9][0-9]*/q/g' \
        -e 's/cff*/R/g' -e 's/1[0-9][0-9]*/r/g' \
        -e 's/daa*/S/g' -e 's/2[0-9][0-9]*/s/g' \
        -e 's/dbb*/T/g' -e 's/3[0-9][0-9]*/t/g' \
        -e 's/dcc*/U/g' -e 's/4[0-9][0-9]*/u/g' \
        -e 's/ddd*/V/g' -e 's/5[0-9][0-9]*/v/g' \
        -e 's/dee*/W/g' -e 's/6[0-9][0-9]*/w/g' \
        -e 's/dff*/X/g' -e 's/7[0-9][0-9]*/x/g' \
        -e 's/eaa*/Y/g' -e 's/8[0-9][0-9]*/y/g' \
        -e 's/ebb*/Z/g' -e 's/9[0-9][0-9]*/z/g' \
        | cut -c -$1
    }

    for hashstr in $@
    do
      compress_hash 6 $hashstr
    done

元のハッシュ値は [a-f0-9] の文字集合ですが、パターンマッチングによって [a-fA-Z0-9] の文字集合に置き替え、桁数を落としています。見たとおり、ある一定のパターンの繰り返しを単一文字に置き換えているので、上のスクリプトでは元ハッシュ値に対する可逆性はありません。さらに、破壊的に圧縮したハッシュ値をcut で任意の桁数に切り落としています。

次に、評価用のコードを示します。

    while :
    do
      hash=$(date +%s | md5sum | cut -f 1 -d ' ')
      compress_hash 6 $hash
      sleep 1
    done

1秒おきに、UNIX タイム文字列を変換して印字する評価コードです。実行すると

    dFc7wa
    vqA5cx
    rc1a1d
    ucufyV
    1d2b8c
    qm9Le4
    6sqc5c
    atavIr
    7bqc5r
    3auf7b
    ....

のように、6 桁の圧縮されたハッシュ値が出てきます。

短縮 URL などの用途に代表されるように、URL セーフな文字列で桁数を抑えつつもある程度のパターン数は稼ぎたい場合に有効かもしれません。なお、元のハッシュ値は基数が a-z0-f の 16 ですが、これは a-zA-Z0-9 の 62 となる為、6 桁でも約 568 億パターンも稼ぐことが可能です。

    # 62^10 = 839299365868340224 (約84京パターン)
    # 62^9  =  13537086546263552 (約1.3京パターン)
    # 62^8  =    218340105584896 (約218兆パターン)
    # 62^7  =      3521614606208 (約3.5兆パターン)
    # 62^6  =        56800235584 (約568億パターン)
    # 62^5  =          916132832 (約9億パターン)
    # 62^4  =           14776336 (約1477万パターン)

6 桁なら長くなく、覚えられない長さではないし、1/568億の確率で衝突するくらいの強さは持ってますので、ファイル管理には十分だと思いました。

なお、a-zA-Z0-9 の文字集合に加えて記号 /+= を許可する場合は、上のようなスクリプトではなくハッシュ値をそのまま base64 エンコードした方が手っ取り早いです。

コメントを残す

メールアドレスが公開されることはありません。