46

在 php 中有没有办法从一个字符串中给出一个唯一的散列,但是散列只由数字组成?

例子:

return md5(234); // returns 098f6bcd4621d373cade4e832627b4f6

但是我需要

return numhash(234); // returns 00978902923102372190 
(20 numbers only)

这里的问题是我希望散列很短。

编辑: 好的,让我在这里解释一下背景故事。我有一个网站,每个注册人都有一个 ID,我还需要一个 ID 供该人使用和交换(因此不能太长),到目前为止 ID 编号为 00001、00002、00003 等。 ..

  1. 这让一些人看起来更重要
  2. 这显示了我不想透露的应用程序信息。

要修复第 1 点和第 2 点,我需要“隐藏”该数字,同时保持其唯一性。

编辑+解决方案:

基于https://stackoverflow.com/a/23679870/175071代码的数字哈希函数

/**
 * Return a number only hash
 * https://stackoverflow.com/a/23679870/175071
 * @param $str
 * @param null $len
 * @return number
 */
public function numHash($str, $len=null)
{
    $binhash = md5($str, true);
    $numhash = unpack('N2', $binhash);
    $hash = $numhash[1] . $numhash[2];
    if($len && is_int($len)) {
        $hash = substr($hash, 0, $len);
    }
    return $hash;
}

// Usage
numHash(234, 20); // always returns 6814430791721596451
4

7 回答 7

66

PHP 中的 MD5 或 SHA1 哈希返回一个十六进制数,因此您需要做的就是转换基数。PHP 有一个函数可以为你做到这一点:

$bignum = hexdec( md5("test") );

或者

$bignum = hexdec( sha1("test") );

十六进制的 PHP 手册

由于您想要有限的尺寸编号,因此您可以使用模块化除法将其放在您想要的范围内。

$smallnum = $bignum % [put your upper bound here]

编辑

正如 Artefacto 在评论中指出的那样,使用这种方法将导致数字超出 PHP 中 Integer 的最大大小,并且模除后的结果将始终为 0。但是,取包含前 16 的哈希的子字符串字符没有这个问题。用于计算初始大数的修订版:

$bignum = hexdec( substr(sha1("test"), 0, 15) );
于 2010-07-31T19:21:59.467 回答
19

你可以试试crc32()。参见文档: http: //php.net/manual/en/function.crc32.php

$checksum = crc32("The quick brown fox jumped over the lazy dog.");
printf("%u\n", $checksum); // prints 2191738434 

话虽如此,crc应该用于validate the integrity of data.

于 2010-07-31T19:25:46.457 回答
15

有一些很好的答案,但对我来说,这些方法似乎很愚蠢。
他们首先强制 php 创建一个十六进制数,然后将其转换回 ( hexdec) 在 BigInteger 中,然后将其缩减为多个字母......这是很多工作!

相反,为什么不

将哈希读取为二进制:

$binhash = md5('[input value]', true);

然后使用

$numhash = unpack('N2', $binhash); //- or 'V2' for little endian

将其转换为两个INTs ($numhash是两个元素的数组)。AND现在您可以简单地使用操作来减少数字中的位数。例如:

$result = $numhash[1] & 0x000FFFFF; //- to get numbers between 0 and 1048575

但要注意碰撞!减少数量意味着增加两个不同[输入值]具有相同输出的概率。

我认为更好的方法是使用带有 Bijectiv 函数的“ID-Crypting”。所以不会发生碰撞!对于最简单的类型,只需使用Affine_cipher

最大输入值范围为 0 到 25 的示例:

function numcrypt($a)
{
   return ($a * 15) % 26;
}

function unnumcrypt($a)
{
   return ($a * 7) % 26;
}

输出:

numcrypt(1) : 15
numcrypt(2) : 4
numcrypt(3) : 19

unnumcrypt(15) : 1
unnumcrypt(4)  : 2
unnumcrypt(19) : 3

例如

$id = unnumcrypt($_GET('userid'));

... do something with the ID ...

echo '<a href="do.php?userid='. numcrypt($id) . '"> go </a>';

当然这不安全,但如果没有人知道用于加密的方法,那么就没有安全原因,那么这种方法更快且碰撞安全。

于 2014-05-15T13:34:57.547 回答
8

切断哈希的问题是冲突,为了避免它尝试:

return  hexdec(crc32("Hello World"));

crc32(): _

生成 str 的 32 位长度的循环冗余校验和多项式。这通常用于验证正在传输的数据的完整性。

这给了我们一个 32 位的整数,在 32 位安装中为负,在 64 位中为正。这个整数可以像 ID 一样存储在数据库中。这没有冲突问题,因为它适合 32 位变量,一旦您使用该hexdec()函数将其转换为十进制。

于 2015-08-19T16:06:28.110 回答
1

首先,md5 基本上已经被破坏了,所以你不应该将它用于除非关键散列之外的任何事情。PHP5 有这个hash()功能,见http://www.php.net/manual/en/function.hash.php

将最后一个参数设置为 true 将为您提供一串二进制数据。或者,您可以将生成的十六进制哈希拆分为 2 个字符,然后将它们分别转换为整数,但我希望这会慢得多。

于 2010-07-31T19:20:12.497 回答
0

尝试hashid
它将一个数字散列为您可以定义的格式。格式包括多少个字符,包括什么字符。
示例:
$hashids->encode(1);
将返回“28630”取决于您的格式,

于 2016-04-05T10:08:06.583 回答
0

只需在下面使用我的手动哈希方法:

将数字(例如 6 位)除以素数 3、5、7。

并获取小数点后的前 6 个值作为要使用的 ID。在实际创建 ID 之前检查唯一性,如果存在冲突,则将最后一位数字增加 +1,直到不发生冲突。
例如 123456 给你 771428 123457 给你 780952 123458 给你 790476。

于 2018-09-26T09:05:34.580 回答