18

これは、データベース内のmd5ハッシュを参照する短縮URLを作成するためのものです。私はこのようなものを変換したいと思います:

a7d2cd9e0e09bebb6a520af48205ced1

このようなものに:

hW9lM5f27

これらは両方ともほぼ同じ量の情報を含んでいます。この方法は、直接的で可逆的である必要はありませんが、それは素晴らしいことです(より柔軟です)。少なくとも、再現可能であるように、シードとして16進ハッシュを使用してランダムに生成された文字列が必要です。考えられる答えはたくさんあると思いますが、人々がどのようにエレガントにそれを行うのか興味があります。

ああ、これは元のハッシュと完全に1:1で対応している必要はありませんが、それはボーナスになります(可逆性の基準ですでにそれを暗示していると思います)。そして、できれば衝突を避けたいと思います。

編集 私は私の最初の計算が完全に間違っていたことに気づきました(ここで答えた人々のおかげですが、手がかりを得るのに時間がかかりました)そしてあなたはすべての小文字と大文字をミックスに投げ込むことによって文字列の長さをあまり減らすことはできません。したがって、16進数から62進数に直接変換されないものが必要になると思います。

4

6 に答える 6

10

考慮すべき小さな関数は次のとおりです。

/** Return 22-char compressed version of 32-char hex string (eg from PHP md5). */
function compress_md5($md5_hash_str) {
    // (we start with 32-char $md5_hash_str eg "a7d2cd9e0e09bebb6a520af48205ced1")
    $md5_bin_str = "";
    foreach (str_split($md5_hash_str, 2) as $byte_str) { // ("a7", "d2", ...)
        $md5_bin_str .= chr(hexdec($byte_str));
    }
    // ($md5_bin_str is now a 16-byte string equivalent to $md5_hash_str)
    $md5_b64_str = base64_encode($md5_bin_str);
    // (now it's a 24-char string version of $md5_hash_str eg "VUDNng4JvrtqUgr0QwXOIg==")
    $md5_b64_str = substr($md5_b64_str, 0, 22);
    // (but we know the last two chars will be ==, so drop them eg "VUDNng4JvrtqUgr0QwXOIg")
    $url_safe_str = str_replace(array("+", "/"), array("-", "_"), $md5_b64_str);
    // (Base64 includes two non-URL safe chars, so we replace them with safe ones)
    return $url_safe_str;
}

基本的に、MD5ハッシュ文字列には16バイトのデータがあります。各バイトは2桁の16進数(つまり00-FF)としてエンコードされるため、32文字の長さになります。したがって、それらをバイトに分割し、16バイトの文字列を作成します。ただし、これは人間が読める形式または有効なASCIIではなくなったため、base-64でエンコードして読みやすい文字に戻します。ただし、base-64では約4/3の拡張が発生するため(入力8ビットあたり6ビットしか出力されないため、24ビットをエンコードするには32ビットが必要です)、16バイトは22バイトになります。ただし、base-64エンコーディングは通常4の倍数の長さにパディングされるため、24文字の出力の最初の22文字しか取得できません(最後の2文字はパディングです)。次に、base-64エンコーディングで使用されるURLセーフではない文字を、同等のURLセーフな文字に置き換えます。

これは完全に元に戻すことができますが、それは読者の練習問題として残されています。

人間が読める形式/ASCIIを気にしない場合を除いて、これが最善の方法だと思います。その場合は、$md5_bin_strを直接使用できます。

また、すべてのビットを保持する必要がない場合は、この関数の結果のプレフィックスまたは他のサブセットを使用できます。データを破棄することは、明らかに物事を短縮する最も簡単な方法です。(しかし、それは元に戻せません)

PS「a7d2cd9e0e09bebb6a520af48205ced1」(32文字)を入力すると、この関数は「VUDNng4JvrtqUgr0QwXO0Q」(22文字)を返します。

于 2010-07-22T23:27:32.000 に答える
5

以下は、任意の入力長に対するBase-16からBase-64への変換と逆Base-64からBase-16への変換の2つの変換関数です。

function base16_to_base64($base16) {
    return base64_encode(pack('H*', $base16));
}
function base64_to_base16($base64) {
    return implode('', unpack('H*', base64_decode($base64)));
}

URLとファイル名に安全なアルファベットを使用したBase-64エンコーディングが必要な場合は、次の関数を使用できます。

function base64_to_base64safe($base64) {
    return strtr($base64, '+/', '-_');
}
function base64safe_to_base64($base64safe) {
    return strtr($base64safe, '-_', '+/');
}

URLセーフ文字を使用して16進MD5値を圧縮する関数が必要な場合は、次を使用できます。

function compress_hash($hash) {
    return base64_to_base64safe(rtrim(base16_to_base64($hash), '='));
}

そして逆関数:

function uncompress_hash($hash) {
    return base64_to_base16(base64safe_to_base64($hash));
}
于 2010-07-23T08:44:51.993 に答える
3

もちろん、自分のニーズを完全に満たす関数が必要な場合は、自分で作成する方がよいでしょう。これが私が思いついたものです。

//takes a string input, int length and optionally a string charset
//returns a hash 'length' digits long made up of characters a-z,A-Z,0-9 or those specified by charset
function custom_hash($input, $length, $charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUFWXIZ0123456789'){
    $output = '';
    $input = md5($input); //this gives us a nice random hex string regardless of input 

    do{
        foreach (str_split($input,8) as $chunk){
            srand(hexdec($chunk));
            $output .= substr($charset, rand(0,strlen($charset)), 1);
        }
        $input = md5($input);

    } while(strlen($output) < $length);

    return substr($output,0,$length);
}

これは非常に汎用的なランダム文字列ジェネレータですが、結果は入力文字列によって決定され、その入力にわずかな変更を加えるとまったく異なる結果が生成されるため、古いランダム文字列ジェネレータだけではありません。あなたはこれであらゆる種類のことをすることができます:

custom_hash('1d34ecc818c4d50e788f0e7a9fd33662', 16); // 9FezqfFBIjbEWOdR
custom_hash('Bilbo Baggins', 5, '0123456789bcdfghjklmnpqrstvwxyz'); // lv4hb
custom_hash('', 100, '01'); 
// 1101011010110001100011111110100100101011001011010000101010010011000110000001010100111000100010101101

誰かがそれに関する問題や改善の余地を見ていますか?

于 2010-07-23T08:32:02.663 に答える
2

単純な古いベース変換を行うことができます。ハッシュは16進数で表され、ハッシュを表現したいサイズのアルファベットを作成できます。Base64はこの目的に適していますが、文字列ではなく値をエンコードするように独自の関数を作成することをお勧めします。

ただし、標準のBase64には、URLに入れたくない文字が含まれていることに注意してください。+、/およびパディング文字=。URLセーフなBase64エンコーディングを取得するために前後に変換するときに、これらの文字を別の文字に置き換えることができます(または、独自の関数を作成する場合は、最初に安全な文字セットを使用します)。

于 2010-07-22T22:52:27.527 に答える
2

私は1-1の通信に反対することをお勧めします:

base-64エンコーディングでは、入力を(4/8)/(6/8)-> 4 / 6〜66%のサイズにしか減らすことができません(これは、「醜い」base64文字を処理することを前提としています新しいものを追加せずに)。

本当に「きれいな」値を取得するには、(二次的な)ルックアップ方法を検討するでしょう。この代替方法を確立したら、その範囲の値(たとえば、乱数)を生成する方法を選択すると、ソースハッシュ値がなくなり(通信が失われるため)、任意の「きれいな」ターゲットセットを使用できます。 、おそらく[az][AZ][0-9]。

除算とキャリーの方法と配列のルックアップに従うだけで、ベース(上記の62)に変換できます。ちょっとした運動が楽しいはずです。

注:[0、62 ^ 5)から乱数を選択すると、エンコードされた出力を完全にパックする(そして32ビット整数値に収まる)値が得られます。次に、このプロセスを連続して複数回実行して、xxx​​xxyyyyyzzzzzz(x、y、zは異なるグループであり、合計値は(62 ^ 5)^ 3の範囲内)などの5の倍数の結果値を取得できます。 -> 62 ^ 15->「巨大な価値」)

コメントのために編集

1-1の対応がなければ、 base62を使用して本当に短いきれいなもの(おそらく8文字の長さの「小さい」)を作成できるため、8文字で最大218340105584896の値を格納できます。これは、おそらくこれまでに必要な値よりも多くなります。または、56800235584の異なる値の保存を「のみ」許可する6文字ですら!(そして、その数値を単純な32ビット整数に格納することはできません:-) 5文字に落とすと、スペースが再び減少します(10億弱:916,132,832)が、今では次のことができるものがあります。符号付き32ビット整数に適合します(多少無駄ですが)。

DBは、この値のインデックスがランダムなソースで「高速フラグメント化」される場合でも、重複がないことを確認する必要があります(ただし、カウンターなどを使用できます)。十分に分散されたPRNGは、十分に広い範囲で競合(読み取り:再試行)を最小限に抑える必要があります(シードをローリングし続け、リセットしない、または適切にリセットしないと仮定します)-Super 7は、サイクル中に重複がないことを保証することもできます(わずか〜32k)ですが、上記のように、ターゲットスペースはまだ大きいです。最小エンコードサイズに関して、1対1の関係を維持するために必要なものの上部にある数学を参照してください。

除算とキャリーの方法は、ソース番号を別のベース(おそらくbase62)に入れる方法を説明するだけです。同じ一般的な方法を適用して、「自然な」ベース(PHPではbase10)から任意のベースに移動できます。

于 2010-07-22T23:29:00.910 に答える
1

それは何であるかに依存しa7d2cd9e0e09bebb6a520af48205ced1ます。から来ているので16進数について話していると仮定するとmd5、を実行するだけで済みますbase64_encode。文字列形式の16進数がある場合は、を実行する必要がありますhexdec。ただし、maxintの問題が発生しないように注意してください。

于 2010-07-22T22:50:27.093 に答える