【问题标题】:Most space efficient encoding that generate printable string in PHP?在 PHP 中生成可打印字符串的最节省空间的编码?
【发布时间】:2013-06-23 13:53:19
【问题描述】:

我有一个大字符串$string,当应用于md5()时,给我

c4ca4238a0b923820dcc509a6f75849b

长度是32,我想减,所以

base64_encode(md5($string, true));

xMpCOKC5I4INzFCab3WEmw==

删除最后两个 == 它给了我一个长度 = 22 的字符串。

还有其他更好的算法吗?

【问题讨论】:

  • 一个选项是压缩字符串。您想在这里实现什么目标?
  • 一般来说,允许使用的字母越大,编码值所需的字符就越少。只有您可以说出您可以使用的字母大小。
  • 你对最终字符串的计划是什么?是为了比较大字符串吗?所以你只想得到一个几乎唯一的哈希值?
  • 有多短才算够短?
  • @Ryan,知道您是否要从“缩短”的字符串中取回原始字符串非常重要。如果是这样,那么 md5 是不行的。

标签: php hash base64 md5


【解决方案1】:

我不确定您是否意识到md5 是一个哈希函数,因此是不可逆的。如果您不关心可逆性,您也可以将md5 哈希(或您喜欢的任何哈希*)修剪为任意数量的字符。所有这一切都会增加碰撞的可能性(我感觉这不会产生均匀分布)。

如果您正在寻找可逆(即非破坏性)压缩,请不要重新发明轮子。使用内置函数,如gzdeflate()gzcompress(),或other similar functions


*这是list of hash functions(维基百科)及其输出的大小。 我想最小的“哈希函数”应该是parity bit :)

【讨论】:

    【解决方案2】:

    一种更好的方法是,不要将二进制转换为十六进制(如 md5 那样),然后将 字符串 转换为 base64,而是将十六进制 md5 直接转换为 base64。

    由于十六进制是每个字符 16 位,base64 是每个字符 64 位,所以每 2 个十六进制字符将组成一个 base64 字符。

    要执行转换,您可以执行以下操作:

    • 将字符串分成 16 个 2 个字符的块
    • 第一个字符应乘以 2 并添加到第二个字符(请记住 A-F = 10-15)。
    • 可以使用此处的表将这个数字与 base64 方案匹配:https://en.wikipedia.org/wiki/Base64

    这将产生一个 16 字符的 base64 字符串,其值与 md5 字符串的十六进制表示形式相同。

    理论上,你可以对任何基地做同样的事情。如果我们有办法用 ASCII 编码 base128 字符串,我们可以得到一个 8 个字符的字符串。不过由于字符集有限,我认为base64是常用的最高基数。

    【讨论】:

    • 更简单的方法:将true 作为第二个参数传递给md5() 以使其返回原始输出。
    【解决方案3】:

    你想要的字符串的length越小..possible combination的个数越小

    有声望的可能性总数

    总可能性 = nr

    由于我们正在处理 base64 具有可打印输出,这意味着我们只有 64 个字符

     n = 64 
    

    如果您正在查看 22 个字母的长度

    nr = 6422 = 5,444,517,870,735,015,415,413,993,718,908,291,383,296 种可能性

    回到你的问题:有没有更好的算法?

    将具有良好散列的字符串截断为所需的长度,因为总的可能性和冲突是固定的

    $string = "the fox jumps over the lazy brown dog";
    echo truncateHash($string, 8);
    

    输出

    9TWbFjOl
    

    使用的功能

    function truncateHash($str, $length) {
        $hash = hash("sha256", $str, true);
        return substr(base64_encode($hash), 0, $length);
    } 
    

    【讨论】:

      【解决方案4】:

      这种编码生成更短的字符串,

      print base64_encode(hash("crc32b",$string,1));
      

      输出

      qfQIdw==
      

      【讨论】:

      • CRC32 是一种不可逆的转换。您不妨截断哈希。
      • @duskwuff md5也是不可逆的变换,都是hashing algorithms
      • 那么,我们有每±2^32 个字符串发生冲突吗? 我喜欢蛮力……
      • @bwoebi 这取决于他想在哪里使用此类哈希的 OP,但在这种特殊情况下,哈希长度似乎至关重要。至于安全问题,有sha256sha512 是弱md5 的替代品
      【解决方案5】:

      不确定 MD5 是否适合您,但我会假设您有理由坚持使用此算法并正在寻找更短的表示。有几种可能性可以生成具有不同字母的较短字符串:

      选项 1:二进制字符串

      MD5 的最短可能形式是它的二进制表示,要获得这样的字符串,您可以简单地调用:

      $binaryMd5 = md5($input, true);
      

      这个字符串可以像数据库中的任何其他字符串一样存储,它只需要 16 个字符。只需确保使用 mysqli_real_escape_string() 或参数化查询 (PDO) 进行适当的转义即可。

      选项 2:Base64 编码

      Base64 编码将生成一个带有以下字母的字符串:[0-9 A-Z a-z + /] 并使用 '=' 作为填充。这种编码速度非常快,但有时会包含不需要的字符“+/=”。

      $base64Md5 = base64_encode(md5($input, true));
      

      对于 MD5 哈希,输出长度始终为 24 个字符。

      选项 3:Base62 编码

      base62 编码仅使用字母表 [0-9 A-Z a-z]。这样的字符串可以安全地用于任何目的,例如 URL 中的标记,而且它们非常紧凑。我写了一个base62 encoder,它能够将二进制字符串转换为base62 字母表。这可能不是最快的实现,但我的目标是编写可理解的代码。同一个类可以很容易地适应不同的字母表。

      $base62Md5 = StoBase62Encoder::base62encode(md5($input, true));
      

      MD5 哈希的输出长度从 16 到 22 个字符不等。

      【讨论】:

        【解决方案6】:

        Base 91 看起来是最节省空间的二进制到 ASCII 可打印编码算法(这似乎是您想要的)。

        我还没有看到 PHP 实现,但如果您的软件必须与其他软件一起使用,我会坚持使用 Base 64;它广为人知,速度极快,随处可用。

        【讨论】:

          【解决方案7】:

          首先,回答您的问题:是的,有更好的算法(如果“更好”是指“更短”)。

          hash() 函数(它是PHP 核心的一部分,自PHP 5.1.2 起默认启用)与adler32fnv132crc32crc32b、@ 中的任何一个一起使用987654329@ 或joaat 算法。

          如果对您当前的情况没有更深入的了解,您不妨选择您认为听起来最酷的那个。

          这是一个例子:

          hash('crc32b', $string)
          

          我设置了an online example你可以玩玩。

          其次,我想指出,您所问的是another question here on stackoverflow 的几乎完全相同的副本。

          【讨论】:

            【解决方案8】:

            我从您的帖子中了解到您正在搜索散列算法而不是压缩。

            在 php 中有各种标准的散列算法。看看 PHP hashing functions。 根据您要散列的内容,有不同的方法。小心计算平均碰撞概率。

            但是,您似乎正在搜索“压缩”,它输出给定字符串的最小可能大小的字符。如果有,请查看Lempel–Ziv–Welch (php implementation) 或others

            【讨论】:

              猜你喜欢
              • 2018-11-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-11-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多