我需要将字符串转换为某种形式的哈希。这在JavaScript中是可能的吗?
我没有使用服务器端语言,所以我不能这样做。
我需要将字符串转换为某种形式的哈希。这在JavaScript中是可能的吗?
我没有使用服务器端语言,所以我不能这样做。
当前回答
子级Crypto.digest
我没有使用服务器端语言,所以我不能这样做。
你确定你不能那样做吗?
你忘了你在使用Javascript吗?
尝试SubleCrypto。它支持SHA-1、SHA-128、SHA-256和SHA-512哈希函数。
异步函数哈希(message/*:string*/){const text_encoder=新的TextEncoder;常量数据=text_encoder.encode(消息);const message_digest=等待window.crypto.intege.digest(“SHA-512”,数据);返回message_digest;}//->阵列缓冲区函数in_hex(data/*:ArrayBuffer*/){const八位字节=新的Uint8Array(数据);const hex=[].map.call(八位字节,八位字节=>八位字节.toString(16).padStart(2,“0”)).join(“”);返回十六进制;}//->字符串(异步函数demo(){console.log(in_hex(等待哈希(“感谢魔法”)));})();
其他回答
基于ES6中接受的答案。更小,可维护,可在现代浏览器中使用。
函数hashCode(str){return str.split(“”).reduce((prevHash,currVal)=>(((prevHash<<5)-previvHash)+currVal.charCodeAt(0))|0,0);}//测试console.log(“hashCode(\“Hello!\”):“,hashCode('Hello!'));
编辑(2019-11-04):
单线箭头功能版本:
consthashCode=s=>s.split(“”).reduce((a,b)=>(((a<<5)-a)+b.charCodeAt(0))|0,0)//测试console.log(hashCode(“您好!”))
詹金斯一次一哈希非常好:
//Credits (modified code): Bob Jenkins (http://www.burtleburtle.net/bob/hash/doobs.html)
//See also: https://en.wikipedia.org/wiki/Jenkins_hash_function
//Takes a string of any size and returns an avalanching hash string of 8 hex characters.
function jenkinsOneAtATimeHash(keyString)
{
let hash = 0;
for (charIndex = 0; charIndex < keyString.length; ++charIndex)
{
hash += keyString.charCodeAt(charIndex);
hash += hash << 10;
hash ^= hash >> 6;
}
hash += hash << 3;
hash ^= hash >> 11;
//4,294,967,295 is FFFFFFFF, the maximum 32 bit unsigned integer value, used here as a mask.
return (((hash + (hash << 15)) & 4294967295) >>> 0).toString(16)
};
示例:
jenkinsOneAtATimeHash('test')
"31c25ec1"
jenkinsOneAtATimeHash('a')
"ca2e9442"
jenkinsOneAtATimeHash('0')
"6e3c5c6b"
您还可以删除末尾的.toString(16)部分以生成数字:
jenkinsOneAtATimeHash2('test')
834821825
jenkinsOneAtATimeHash2('a')
3392050242
jenkinsOneAtATimeHash2('0')
1849449579
请注意,如果您不需要对字符串或键进行哈希,而只需要凭空生成哈希,则可以使用:
window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
示例:
window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
"6ba9ea7"
window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
"13fe7edf"
window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
"971ffed4"
与上面相同,您可以删除末尾的`.toString(16)部分以生成数字:
window.crypto.getRandomValues(new Uint32Array(1))[0]
1154752776
window.crypto.getRandomValues(new Uint32Array(1))[0]
3420298692
window.crypto.getRandomValues(new Uint32Array(1))[0]
1781389127
注意:您也可以使用此方法一次生成多个值,例如:
window.crypto.getRandomValues(new Uint32Array(3))
Uint32Array(3) [ 2050530949, 3280127172, 3001752815 ]
@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;
}
得益于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);
}
注意:即使使用最好的32位哈希,冲突也迟早会发生。哈希冲突概率可以计算为,近似为(参见此处)。这可能比直觉所暗示的更高:假设32位哈希和k=10000个项目,则发生冲突的概率为1.2%。77163个样本的概率为50%!(计算器)。我建议在底部使用变通方法。
在回答这个问题时哪种哈希算法最适合唯一性和速度?,伊恩·博伊德发表了一篇很好的深入分析。简而言之(正如我所解释的那样),他得出的结论是MurmurHash是最好的,其次是FNV-1a。esmiralha提出的Java String.hashCode()算法似乎是DJB2的变体。
FNV-1a的分布比DJB2更好,但速度较慢DJB2比FNV-1a更快,但倾向于产生更多的碰撞MurmurHash3比DJB2和FNV-1a更好更快(但优化的实现需要比FNV和DJB2更多的代码行)
这里有一些输入字符串较大的基准测试:http://jsperf.com/32-bit-hash当对短输入字符串进行散列处理时,相对于DJ2B和FNV-1a,杂音的性能会下降:http://jsperf.com/32-bit-hash/3
因此,总的来说,我会推荐杂音3。请参阅此处了解JavaScript实现:https://github.com/garycourt/murmurhash-js
如果输入字符串很短,性能比分发质量更重要,请使用DJB2(如esmiralha接受的答案所建议的)。
如果质量和小代码大小比速度更重要,我使用FNV-1a的这个实现(基于这个代码)。
/**
* Calculate a 32 bit FNV-1a hash
* Found here: https://gist.github.com/vaiorabbit/5657561
* Ref.: http://isthe.com/chongo/tech/comp/fnv/
*
* @param {string} str the input value
* @param {boolean} [asString=false] set to true to return the hash value as
* 8-digit hex string instead of an integer
* @param {integer} [seed] optionally pass the hash of the previous chunk
* @returns {integer | string}
*/
function hashFnv32a(str, asString, seed) {
/*jshint bitwise:false */
var i, l,
hval = (seed === undefined) ? 0x811c9dc5 : seed;
for (i = 0, l = str.length; i < l; i++) {
hval ^= str.charCodeAt(i);
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
}
if( asString ){
// Convert to 8 digit hex string
return ("0000000" + (hval >>> 0).toString(16)).substr(-8);
}
return hval >>> 0;
}
提高碰撞概率
如这里所解释的,我们可以使用此技巧扩展哈希位大小:
function hash64(str) {
var h1 = hash32(str); // returns 32 bit (as 8 byte hex string)
return h1 + hash32(h1 + str); // 64 bit (as 16 byte hex string)
}
小心使用,但不要期望太多。