我需要将字符串转换为某种形式的哈希。这在JavaScript中是可能的吗?
我没有使用服务器端语言,所以我不能这样做。
我需要将字符串转换为某种形式的哈希。这在JavaScript中是可能的吗?
我没有使用服务器端语言,所以我不能这样做。
当前回答
这里是一个紧凑的ES6友好可读片段
const stringHashCode = str => {
let hash = 0
for (let i = 0; i < str.length; ++i)
hash = Math.imul(31, hash) + str.charCodeAt(i)
return hash | 0
}
其他回答
这里的许多答案都是取自Java的String.hashCode哈希函数。它可以追溯到1981年的Gosling Emacs,它非常脆弱,在现代JavaScript中表现得毫无意义。事实上,通过使用ES6 Math.imul,实现速度可能会大大加快,但没有人注意到。我们可以在基本相同的性能下做得更好。
这是我做的一个cryb53,一个简单但高质量的53位散列。它非常快,提供了非常好的*哈希分布,并且因为它输出53位,所以与任何32位哈希相比,具有明显更低的冲突率。此外,您可以忽略SA的CC许可证,因为它是我GitHub上的公共域。
常量cyrb53=(str,种子=0)=>{设h1=0xdeadbeef^种子,h2=0x41c6ce57^种子;for(设i=0,ch;i<str.length;i++){ch=str.charCodeAt(i);h1=数学模拟(h1^ch,2654435761);h2=数学模拟(h2^ch,1597334677);}h1=数学模拟(h1^(h1>>16),2246822507)^数学模拟(h2^(h2>>13),3266489909);h2=数学模拟(h2^(h2>>16),2246822507)^数学模拟(h1^(h1>>13),3266489909);返回4294967296*(2097151&h2)+(h1>>>0);};console.log(`cyrb53('a')->${cyrb53“'a')}`)console.log(`cyrb53('b')->${cyrb53console.log(`cyrb53('return')->${cyrb53('reten')}`)console.log(`cyrb53('resident')->${cyrb53console.log(`cyrb53('resident',1)->${cyrb53console.log(`cyrb53('resident',2)->${cyrb53console.log(`cyrb53('resident',3)->${cyrb53
*它大致类似于众所周知的MurmurHash/xxHash算法。它使用乘法和Xorshift的组合来生成哈希,但并不彻底。因此,它比JavaScript中的任何一种都要快,实现起来也要简单得多,但可能无法通过SMHasher中的所有测试。这不是加密哈希函数,因此不要将其用于安全目的。
与任何适当的哈希一样,它具有雪崩效应,这基本上意味着输入中的小变化会导致输出中的大变化,从而使生成的哈希看起来更“随机”:
"501c2ba782c97901" = cyrb53("a")
"459eda5bc254d2bf" = cyrb53("b")
"fbce64cc3b748385" = cyrb53("revenge")
"fb1d85148d13f93a" = cyrb53("revenue")
您可以选择为相同输入的交替流提供种子(无符号整数,最大32位):
"76fee5e6598ccd5c" = cyrb53("revenue", 1)
"1f672e2831253862" = cyrb53("revenue", 2)
"2b10de31708e6ab7" = cyrb53("revenue", 3)
从技术上讲,它是一个64位散列,即两个不相关的32位散列并行计算,但JavaScript限于53位整数。如果方便,可以通过使用十六进制字符串或数组更改return语句来使用完整的64位输出。
return [h2>>>0, h1>>>0];
// or
return (h2>>>0).toString(16).padStart(8,0)+(h1>>>0).toString(16).padStart(8,0);
// or
return 4294967296n * BigInt(h2) + BigInt(h1);
请注意,构造十六进制字符串会大大降低批处理速度。该阵列效率更高,但显然需要两次检查而不是一次。我还包括BigInt,它应该比String稍快,但仍然比Array或Number慢得多。
为了好玩,这里有TinySimpleHash,这是我能想出的最小的哈希,它仍然很不错。这是一个89个字符的32位哈希,随机性比FNV或DJB2更好:
TSH=s=>{for(var i=0,h=9;i<s.length;)h=Math.imul(h^s.charCodeAt(i++),9**9);return h^h>>>9}
得益于mar10的示例,我找到了一种在C#和Javascript中为FNV-1a获得相同结果的方法。如果存在unicode字符,为了提高性能,将放弃上面的部分。不知道为什么在哈希时维护这些路径会很有用,因为我现在只哈希url路径。
C#版本
private static readonly UInt32 FNV_OFFSET_32 = 0x811c9dc5; // 2166136261
private static readonly UInt32 FNV_PRIME_32 = 0x1000193; // 16777619
// Unsigned 32bit integer FNV-1a
public static UInt32 HashFnv32u(this string s)
{
// byte[] arr = Encoding.UTF8.GetBytes(s); // 8 bit expanded unicode array
char[] arr = s.ToCharArray(); // 16 bit unicode is native .net
UInt32 hash = FNV_OFFSET_32;
for (var i = 0; i < s.Length; i++)
{
// Strips unicode bits, only the lower 8 bits of the values are used
hash = hash ^ unchecked((byte)(arr[i] & 0xFF));
hash = hash * FNV_PRIME_32;
}
return hash;
}
// Signed hash for storing in SQL Server
public static Int32 HashFnv32s(this string s)
{
return unchecked((int)s.HashFnv32u());
}
JavaScript版本
var utils = utils || {};
utils.FNV_OFFSET_32 = 0x811c9dc5;
utils.hashFnv32a = function (input) {
var hval = utils.FNV_OFFSET_32;
// Strips unicode bits, only the lower 8 bits of the values are used
for (var i = 0; i < input.length; i++) {
hval = hval ^ (input.charCodeAt(i) & 0xFF);
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
}
return hval >>> 0;
}
utils.toHex = function (val) {
return ("0000000" + (val >>> 0).toString(16)).substr(-8);
}
String.prototype.hashCode=函数(){var散列=0,i、 chr;如果(this.length==0)返回哈希;对于(i=0;i<this.length;i++){chr=this.charCodeAt(i);哈希=((哈希<<5)-哈希)+chr;哈希|=0;//转换为32位整数}返回哈希;}const str='收入'console.log(str,str.hashCode())
来源
EDIT
根据我的jsperf测试,公认的答案实际上更快:http://jsperf.com/hashcodelordvlad
原始的,原始的
如果有人感兴趣,这里有一个改进的(更快的)版本,它将在缺少reduce数组功能的旧浏览器上失败。
hashCode=函数{return s.split(“”).reduce(函数(a,b){a=((a<<5)-a)+b.charCodeAt(0);返回a&a;}, 0);}//测试console.log(hashCode(“hello”));console.log(hashCode(“这是一个文本。”));console.log(hashCode(“Luis Fonsi的Despacito”));
单线箭头功能版本:
hashCode=s=>s.split(“”).reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);返回a&a},0)//测试console.log(hashCode(“hello”));console.log(hashCode(“这是一个文本。”));console.log(hashCode(“Luis Fonsi的Despacito”));
@esmiralha答案的略微简化版本。
我不会在这个版本中重写String,因为这可能会导致一些不希望的行为。
function hashCode(str) {
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = ~~(((hash << 5) - hash) + str.charCodeAt(i));
}
return hash;
}