从Java脚本中的字符串生成哈希

我需要将字符串转换为某种形式的哈希。这在JavaScript中可行吗?

我没有使用服务器端语言,所以我不能那样做。

前端猿2020/03/12 14:50:43

这应该比其他答案更安全,但在函数中,没有任何预加载源

我基本上创建了sha1的简化版
您获取字符串的字节并将它们按4到32位的“单词”分组,
然后将每8个单词扩展到40个单词(以对结果产生更大的影响)。
这将转到哈希函数(最后一个减少),在该函数中我们对当前状态和输入进行一些数学运算。我们总是得到4个字。
这几乎是一个使用map,reduce ...而不是循环的单命令/单行版本,但是它仍然相当快

String.prototype.hash = function(){
    var rot = (word, shift) => word << shift | word >>> (32 - shift);
    return unescape(encodeURIComponent(this.valueOf())).split("").map(char =>
            char.charCodeAt(0)
        ).reduce((done, byte, idx, arr) =>
            idx % 4 == 0 ? [...done, arr.slice(idx, idx + 4)] : done
        , []).reduce((done, group) =>
            [...done, group[0] << 24 | group[1] << 16 | group[2] << 8 | group[3]]
        , []).reduce((done, word, idx, arr) =>
            idx % 8 == 0 ? [...done, arr.slice(idx, idx + 8)] : done
        , []).map(group => {
            while(group.length < 40)
                group.push(rot(group[group.length - 2] ^ group[group.length - 5] ^ group[group.length - 8], 3));
            return group;
        }).flat().reduce((state, word, idx, arr) => {
            var temp = ((state[0] + rot(state[1], 5) + word + idx + state[3]) & 0xffffffff) ^ state[idx % 2 == 0 ? 4 : 5](state[0], state[1], state[2]);
            state[0] = rot(state[1] ^ state[2], 11);
            state[1] = ~state[2] ^ rot(~state[3], 19);
            state[2] = rot(~state[3], 11);
            state[3] = temp;
            return state;
        }, [0xbd173622, 0x96d8975c, 0x3a6d1a23, 0xe5843775,
            (w1, w2, w3) => (w1 & rot(w2, 5)) | (~rot(w1, 11) & w3),
            (w1, w2, w3) => w1 ^ rot(w2, 5) ^ rot(w3, 11)]
        ).slice(0, 4).map(p =>
            p >>> 0
        ).map(word =>
            ("0000000" + word.toString(16)).slice(-8)
        ).join("");
};

我们还将输出转换为十六进制以获取字符串而不是单词数组。
用法很简单。样品"a string".hash()将返回"88a09e8f9cc6f8c71c4497fbb36f84cd"

String.prototype.hash = function(){
	var rot = (word, shift) => word << shift | word >>> (32 - shift);
	return unescape(encodeURIComponent(this.valueOf())).split("").map(char =>
			char.charCodeAt(0)
		).reduce((done, byte, idx, arr) =>
			idx % 4 == 0 ? [...done, arr.slice(idx, idx + 4)] : done
		, []).reduce((done, group) =>
			[...done, group[0] << 24 | group[1] << 16 | group[2] << 8 | group[3]]
		, []).reduce((done, word, idx, arr) =>
			idx % 8 == 0 ? [...done, arr.slice(idx, idx + 8)] : done
		, []).map(group => {
			while(group.length < 40)
				group.push(rot(group[group.length - 2] ^ group[group.length - 5] ^ group[group.length - 8], 3));
			return group;
		}).flat().reduce((state, word, idx, arr) => {
			var temp = ((state[0] + rot(state[1], 5) + word + idx + state[3]) & 0xffffffff) ^ state[idx % 2 == 0 ? 4 : 5](state[0], state[1], state[2]);
			state[0] = rot(state[1] ^ state[2], 11);
			state[1] = ~state[2] ^ rot(~state[3], 19);
			state[2] = rot(~state[3], 11);
			state[3] = temp;
			return state;
		}, [0xbd173622, 0x96d8975c, 0x3a6d1a23, 0xe5843775,
			(w1, w2, w3) => (w1 & rot(w2, 5)) | (~rot(w1, 11) & w3),
			(w1, w2, w3) => w1 ^ rot(w2, 5) ^ rot(w3, 11)]
		).slice(0, 4).map(p =>
			p >>> 0
		).map(word =>
			("0000000" + word.toString(16)).slice(-8)
		).join("");
};
let str = "the string could even by empty";
console.log(str.hash())//9f9aeca899367572b875b51be0d566b5

西门小小2020/03/12 14:50:43

微妙的加密摘要

我没有使用服务器端语言,所以我不能那样做。

你肯定你不能这样做

您是否忘了您正在使用Java语言(一种不断发展的语言)?

尝试SubtleCrypto它支持SHA-1,SHA-128,SHA-256和SHA-512哈希函数。


async function hash(message/*: string */) {
	const text_encoder = new TextEncoder;
	const data = text_encoder.encode(message);
	const message_digest = await window.crypto.subtle.digest("SHA-512", data);
	return message_digest;
} // -> ArrayBuffer

function in_hex(data/*: ArrayBuffer */) {
	const octets = new Uint8Array(data);
	const hex = [].map.call(octets, octet => octet.toString(16).padStart(2, "0")).join("");
	return hex;
} // -> string

(async function demo() {
	console.log(in_hex(await hash("Thanks for the magic.")));
})();

小小飞云2020/03/12 14:50:42

添加此代码是因为还没有人这样做,并且似乎需要使用散列方法来实现它,但是它总是做得很差。

这需要一个字符串输入,以及您希望哈希值等于的最大数字,并根据字符串输入生成一个唯一的数字。

您可以使用它来生成图像数组的唯一索引(如果您要为用户返回特定的化身,该化身是随机选择的,但也是根据其名称选择的,因此它将始终分配给具有该名称的人)。

当然,您也可以使用此方法将索引返回到颜色数组中,例如根据某人的名字生成唯一的头像背景颜色。

function hashInt (str, max = 1000) {
    var hash = 0;
    for (var i = 0; i < str.length; i++) {
      hash = ((hash << 5) - hash) + str.charCodeAt(i);
      hash = hash & hash;
    }
    return Math.round(max * Math.abs(hash) / 2147483648);
}
Stafan番长2020/03/12 14:50:42

@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;
}
小胖Itachi2020/03/12 14:50:42

如果要避免冲突,则可能需要使用安全散列,例如SHA-256有几种JavaScript SHA-256实现。

我编写了测试以比较几种哈希实现,请参阅https://github.com/brillout/test-javascript-hash-implementations

或访问http://brillout.github.io/test-javascript-hash-implementations/来运行测试。

阳光Itachi2020/03/12 14:50:42

一种快速简洁的方法,从这里改编而成

String.prototype.hashCode = function() {
  var hash = 5381, i = this.length
  while(i)
    hash = (hash * 33) ^ this.charCodeAt(--i)
  return hash >>> 0;
}
斯丁Sam2020/03/12 14:50:42

我基于FNV Multiply+Xor方法的快速(很长)衬垫

my_string.split('').map(v=>v.charCodeAt(0)).reduce((a,v)=>a+((a<<7)+(a<<3))^v).toString(16);
村村阿飞2020/03/12 14:50:42

我需要类似的功能(但有所不同),以便根据用户名和当前时间生成唯一的ID。所以:

window.newId = ->
  # create a number based on the username
  unless window.userNumber?
    window.userNumber = 0
  for c,i in window.MyNamespace.userName
    char = window.MyNamespace.userName.charCodeAt(i)
    window.MyNamespace.userNumber+=char
  ((window.MyNamespace.userNumber + Math.floor(Math.random() * 1e15) + new Date().getMilliseconds()).toString(36)).toUpperCase()

产生:

2DVFXJGEKL
6IZPAKFQFL
ORGOENVMG
... etc 

编辑2015年6月:对于新代码,我使用shortid:https://www.npmjs.com/package/shortid

A小卤蛋Pro2020/03/12 14:50:42

感谢mar10的示例,我找到了一种在FNV-1a的C#AND Javascript中获得相同结果的方法。如果存在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);
}
小小卡卡西2020/03/12 14:50:42

这是一种精致且性能更好的变体:

String.prototype.hashCode = function() {
    var hash = 0, i = 0, len = this.length;
    while ( i < len ) {
        hash  = ((hash << 5) - hash + this.charCodeAt(i++)) << 0;
    }
    return hash;
};

这符合Java对标准的实现 object.hashCode()

这也是只返回正哈希码的代码:

String.prototype.hashcode = function() {
    return (this.hashCode() + 2147483647) + 1;
};

这是Java的匹配项,它只返回正的哈希码:

public static long hashcode(Object obj) {
    return ((long) obj.hashCode()) + Integer.MAX_VALUE + 1l;
}

请享用!

镜风Davaid2020/03/12 14:50:42
String.prototype.hashCode = function() {
  var hash = 0, i, chr;
  if (this.length === 0) return hash;
  for (i = 0; i < this.length; i++) {
    chr   = this.charCodeAt(i);
    hash  = ((hash << 5) - hash) + chr;
    hash |= 0; // Convert to 32bit integer
  }
  return hash;
};

来源:http//werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/

猿乐2020/03/12 14:50:42

如果对任何人reduce都有用,我会将前两个答案组合成一个允许浏览器使用的较旧版本,如果可用,它将使用快速版本,如果没有,则使用esmiralha的解决方案。

/**
 * @see http://stackoverflow.com/q/7616461/940217
 * @return {number}
 */
String.prototype.hashCode = function(){
    if (Array.prototype.reduce){
        return this.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);              
    } 
    var hash = 0;
    if (this.length === 0) return hash;
    for (var i = 0; i < this.length; i++) {
        var character  = this.charCodeAt(i);
        hash  = ((hash<<5)-hash)+character;
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
}

用法就像:

var hash = new String("some string to be hashed").hashCode();
2020/03/12 14:50:42

编辑

根据我的jsperf测试,接受的答案实际上更快:http ://jsperf.com/hashcodelordvlad

原版的

如果有人感兴趣,这是一个改进的(更快)版本,它将在缺少reduce数组功能的旧版浏览器上失败

hashCode = function(s){
  return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);              
}

单线箭头功能版本:

hashCode = s => s.split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0)
番长GO2020/03/12 14:50:42

几乎一半的答案是Java的实现,String.hashCode既不是高质量的也不是超快的。没什么特别的,每个字符仅乘以31。它可以在一行中简单有效地实现,并且使用以下命令可以更快Math.imul

hashCode=s=>{for(var i=0,h;i<s.length;i++)h=Math.imul(31,h)+s.charCodeAt(i)|0;return h}

有了这些,就更好了— cyrb53,一个简单但高质量的53位哈希。任何 32位哈希相比,它速度非常快,提供了很好的哈希分布,并且冲突率大大降低

const cyrb53 = function(str, seed = 0) {
    let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
    for (let i = 0, ch; i < str.length; i++) {
        ch = str.charCodeAt(i);
        h1 = Math.imul(h1 ^ ch, 2654435761);
        h2 = Math.imul(h2 ^ ch, 1597334677);
    }
    h1 = Math.imul(h1 ^ h1>>>16, 2246822507) ^ Math.imul(h2 ^ h2>>>13, 3266489909);
    h2 = Math.imul(h2 ^ h2>>>16, 2246822507) ^ Math.imul(h1 ^ h1>>>13, 3266489909);
    return 4294967296 * (2097151 & h2) + (h1>>>0);
};

与众所周知的MurmurHash / xxHash算法相似,它使用乘法和Xorshift的组合来生成哈希,但是不够彻底。结果,它比JavaScript中的任何一种都要快,并且实现起来也很简单。

它实现了雪崩(非严格),这基本上意味着输入的细微变化在输出中的大变化,从而使得结果散列看起来是随机的:

0xc2ba782c97901 = cyrb53("a")
0xeda5bc254d2bf = cyrb53("b")
0xe64cc3b748385 = cyrb53("revenge")
0xd85148d13f93a = cyrb53("revenue")

您还可以为相同输入的备用流提供种子:

0xee5e6598ccd5c = cyrb53("revenue", 1)
0x72e2831253862 = cyrb53("revenue", 2)
0x0de31708e6ab7 = cyrb53("revenue", 3)

从技术上讲,它是一个64位哈希(并行的是两个不相关的32位哈希),但是JavaScript限于53位整数。如果需要,可以通过更改十六进制字符串或数组的返回行来使用完整的64位输出

请注意,在性能至关重要的情况下,构造十六进制字符串会大大减慢批处理的速度。

return (h2>>>0).toString(16).padStart(8,0)+(h1>>>0).toString(16).padStart(8,0);
// or
return [h2>>>0, h1>>>0];

只是为了好玩,这是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}