Tạo Hash từ chuỗi trong Javascript


586

Tôi cần chuyển đổi chuỗi thành một số dạng băm. Điều này có thể có trong JavaScript không?

Tôi không sử dụng ngôn ngữ phía máy chủ nên tôi không thể làm theo cách đó.


7
MD5 không an toàn, vì vậy đừng tìm cái đó.
henrikstroem

166
@henrikstroem Phụ thuộc vào những gì bạn đang băm; không có gì sai khi sử dụng md5 để tạo hàm băm cho các mục đích không bảo mật.
Brad Koch

7
@BradKoch Phụ thuộc vào những gì bạn đang làm; Không có gì sai khi sử dụng md5 cho mục đích bảo mật. Chắc chắn có các phương pháp tốt hơn để băm mật khẩu, nhưng md5 chỉ tốt khi làm những việc như ký URL.
Paul Ferrett

81
Tôi thấy buồn cười là trong khi MD5 bị chỉ trích trong các bình luận ở đây, hầu như tất cả các câu trả lời đều đề xuất thuật toán băm tồi tệ hơn nhiều và nhận được rất nhiều sự ủng hộ.
Domen

38
Sử dụng MD5 để xác minh rằng bản tải xuống còn nguyên vẹn sẽ không gửi email mật khẩu của bạn cho tất cả đồng nghiệp.
James M. Lay

Câu trả lời:


789
Object.defineProperty(String.prototype, 'hashCode', {
  value: function() {
    var hash = 0, i, chr;
    for (i = 0; i < this.length; i++) {
      chr   = this.charCodeAt(i);
      hash  = ((hash << 5) - hash) + chr;
      hash |= 0; // Convert to 32bit integer
    }
    return hash;
  }
});

Nguồn: http://werxltd.com/wp/2010/05/13/javascript-imcellenceation-of-javas-opes-hashcode-method/


22
Đây là cùng một được sử dụng trong Java. Điều hash << 5 - hashnày giống như hash * 31 + charnhưng rất nhiều nhanh hơn. Thật tuyệt vì nó quá nhanh, và 31 là một nguyên tố nhỏ. Giành chiến thắng ở đó.
corsiKa

41
Tôi đã thực hiện một vài thử nghiệm trên jsperf ( jsperf.com/hashing-strings ) và hàm bitwise thực sự chậm hơn hàm dựa trên số.
skerit

17
@PeterAronZentai Tại sao nó "không sử dụng được"? Đầu ra được tạo bởi mã dựa trên số (hash * 31) + chargiống hệt với đầu ra được tạo bởi mã dựa trên thay đổi ((hash<<5)-hash)+char, ngay cả đối với các chuỗi rất dài (tôi đã kiểm tra nó với các chuỗi chứa hơn một triệu ký tự), vì vậy về mặt không "không sử dụng được" độ chính xác. Độ phức tạp là O (n) cho cả phiên bản dựa trên số và dịch chuyển, do đó, nó không "không sử dụng được" về độ phức tạp.
TachyonVortex

13
Bất cứ ai có thể nhận xét về tính duy nhất (hoặc không) của đầu ra? Cụ thể, nếu tôi chỉ sử dụng hàm băm này cho các chuỗi có độ dài nhỏ hơn n, thì giá trị lớn nhất nmà tôi không thể có xung đột là bao nhiêu?
Don McCurdy

34
Có bất kỳ lý do nào mà nhu cầu này (hoặc nên) có trên nguyên mẫu String không? Nó sẽ không hiệu quả / hiệu quả hơn nếu chỉ có ví dụ; var hashCode = function hashCode (str) {etc...}? Rồi dùng như hashCode("mystring")thế nào?
rattray

146

BIÊN TẬP

dựa trên các bài kiểm tra jsperf của tôi, câu trả lời được chấp nhận thực sự nhanh hơn: http://jsperf.com/hashcodelordvlad

NGUYÊN

Nếu bất cứ ai quan tâm, đây là phiên bản cải tiến (nhanh hơn), sẽ thất bại trên các trình duyệt cũ thiếu reducechức năng mảng.

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

phiên bản chức năng mũi tên một lớp lót:

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

3
Có cách nào để lấy băm là số dương không?
Prosto Trader

46
kỳ dị. tôi chỉ thử nó và hóa ra là chậm hơn so với câu trả lời được chấp nhận. jsperf.com/hashcodelordvlad
chúa tể 18/12/13

113
Anh chàng tốt bụng @lordvlad, thực sự đang thử nghiệm câu trả lời của chính mình, và sau đó báo cáo khi nó chậm hơn.
mikemaccana

9
Tôi mới nhận ra: Thật hoàn hảo khi câu trả lời được chấp nhận nhanh hơn, bởi vì phiên bản của tôi phải biến chuỗi thành một mảng trước, phân bổ bộ nhớ mới và sao chép mọi ký tự ...
lordvlad 22/215

5
[] .reduce.call (str, (p, c, i, a) => (p << 5) - p + a.charCodeAt (i), 0);
Chóng mặt

108

Lưu ý: Ngay cả với hàm băm 32 bit tốt nhất, va chạm sẽ xảy ra sớm hay muộn.

Xác suất va chạm băm có thể được tính như 1 - e ^ (-k (k-1) / 2N, gần đúng như k ^ 2 / 2N ( xem tại đây ). Điều này có thể cao hơn so với trực giác cho thấy:
Giả sử hàm băm 32 bit và k = 10.000 vật phẩm, một vụ va chạm sẽ xảy ra với xác suất 1,2%. Đối với 77.163 mẫu, xác suất trở thành 50%! ( máy tính ).
Tôi đề nghị một cách giải quyết ở phía dưới.

Trong câu trả lời cho câu hỏi này Thuật toán băm nào là tốt nhất cho tính duy nhất và tốc độ? , Ian Boyd đã đăng một bài phân tích chuyên sâu . Nói tóm lại (như tôi giải thích), anh ta đi đến kết luận rằng Murmur là tốt nhất, tiếp theo là FNV-1a.
Thuật toán String.hashCode () của Java mà esmirusha đề xuất dường như là một biến thể của DJB2.

  • FNV-1a có phân phối tốt hơn DJB2, nhưng chậm hơn
  • DJB2 nhanh hơn FNV-1a, nhưng có xu hướng tạo ra nhiều va chạm hơn
  • MurmurHash3 tốt hơn và nhanh hơn DJB2 và FNV-1a (nhưng việc triển khai được tối ưu hóa đòi hỏi nhiều dòng mã hơn FNV và DJB2)

Một số điểm chuẩn có chuỗi đầu vào lớn tại đây: http://jsperf.com/32-bit-hash
Khi chuỗi đầu vào ngắn được băm, hiệu suất của murmur giảm, liên quan đến DJ2B và FNV-1a: http://jsperf.com/32- băm bit / 3

Vì vậy, nói chung tôi muốn giới thiệu murmur3.
Xem tại đây để triển khai JavaScript: https://github.com/ÿcourt / murmurhash- js

Nếu các chuỗi đầu vào ngắn và hiệu suất quan trọng hơn chất lượng phân phối, hãy sử dụng DJB2 (theo đề xuất của câu trả lời được chấp nhận bởi esmirusha).

Nếu chất lượng và kích thước mã nhỏ quan trọng hơn tốc độ, tôi sử dụng triển khai FNV-1a này (dựa trên mã này ).

/**
 * 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;
}

Cải thiện xác suất va chạm

Như đã giải thích ở đây , chúng ta có thể mở rộng kích thước bit băm bằng thủ thuật này:

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)
}

Sử dụng cẩn thận và đừng mong đợi quá nhiều mặc dù.


Tại sao bạn làm ("0000000" + (hval >>> 0).toString(16)).substr(-8);vậy Điều đó có giống như (hval >>> 0).toString(16)không?
Manuel Meker

3
điều này thêm hàng đầu '0' để hàm băm kết quả luôn dài 8 ký tự. Dễ đọc và nhận ra kết quả đầu ra hơn, nhưng đó là ý kiến ​​cá nhân của tôi
mar10

À, tôi hiểu rồi. Đối với nhỏ hval, (hval >>> 0).toString(16)có thể có ít hơn 8 ký tự, vì vậy bạn đệm nó bằng số không. Tôi chỉ bối rối vì (hval >>> 0).toString(16)luôn dẫn đến một chuỗi 8 ký tự chính xác cho tôi.
Manuel Meker

3
Tôi thích câu trả lời này vì nó tạo ra một hàm băm phân phối tốt hơn nhiều: các hàm khác được đề xuất ở đây sẽ tạo ra các giá trị băm do đó. Ví dụ: `hash (" example1 ") - hash (" example2 ") == 1", trong khi điều này khó đoán hơn nhiều.
GavinoGrifoni

1
Đáp lại "FNV-1a có phân phối tốt hơn DJB2, nhưng chậm hơn" - Tôi nghĩ rằng nên nói rằng FNV1a có thể cực kỳ nhanh khi được thực hiện bằng Math.imulchức năng ES6 . Điều đó một mình làm cho nó điểm chuẩn hàng đầu, và cuối cùng là một lựa chọn tốt hơn so với DJB2 về lâu dài.
bryc

64

Dựa trên câu trả lời được chấp nhận trong ES6. Nhỏ hơn, có thể bảo trì và làm việc trong các trình duyệt hiện đại.

function hashCode(str) {
  return str.split('').reduce((prevHash, currVal) =>
    (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0);
}

// Test
console.log("hashCode(\"Hello!\"): ", hashCode('Hello!'));

EDIT (2019-11-04) :

phiên bản chức năng mũi tên một lớp lót:

const hashCode = s => s.split('').reduce((a,b) => (((a << 5) - a) + b.charCodeAt(0))|0, 0)

// test
console.log(hashCode('Hello!'))


1
Cảm ơn đã chia sẻ tôi đã thêm str += ""trước khi băm để tránh ngoại lệ str.split is not a functionbị ném khi các chuỗi không được truyền dưới dạng tham số
BeetleJuice

4
Nhưng nhiều, chậm hơn nhiều so với bất kỳ thứ nào trong số này: https://jsperf.com/hashing-strings
AndyO

Tôi cũng chỉ nhận thấy rằng giải pháp "retro" nhanh nhất cũng thực sự nhỏ hơn nếu bạn loại bỏ các nguồn cấp dữ liệu để nó chỉ dài 3 dòng.
AndyO

2
Bất kỳ cách nào để có sản phẩm này chỉ có kết quả tích cực nhưng vẫn độc đáo?
DIDS

3
@deekshith Câu trả lời được chấp nhận sử dụng hash |= 0để chuyển đổi thành int 32 bit. Việc thực hiện này không. Đây có phải là một lỗi?
Sukima

48

Gần một nửa câu trả lời là các triển khai của Java String.hashCode, không phải là chất lượng cao hay siêu nhanh. Không có gì quá đặc biệt, nó chỉ nhân lên 31 cho mỗi nhân vật. Nó có thể được thực hiện đơn giản và hiệu quả trong một dòng và nhanh hơn nhiều với Math.imul:

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

Ngoài ra, đây là một thứ gì đó tốt hơn trên mạng cyberb53 , một hàm băm 53 bit đơn giản nhưng chất lượng cao. Nó khá nhanh, cung cấp phân phối băm rất tốt và có tỷ lệ va chạm thấp hơn đáng kể so với bất kỳ hàm băm 32 bit nào.

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);
};

Tương tự như thuật toán MurmurHash / xxHash nổi tiếng, nó sử dụng kết hợp phép nhân và Xorshift để tạo ra hàm băm, nhưng không triệt để. Kết quả là nó nhanh hơn cả JavaScript và đơn giản hơn để thực hiện.

Nó đạt được tuyết lở (không nghiêm ngặt), về cơ bản có nghĩa là những thay đổi nhỏ trong đầu vào có những thay đổi lớn trong đầu ra, làm cho hàm băm kết quả xuất hiện ngẫu nhiên:

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

Bạn cũng có thể cung cấp một hạt giống cho các luồng thay thế của cùng một đầu vào:

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

Về mặt kỹ thuật, đó là hàm băm 64 bit (song song hai hàm băm 32 bit không tương thích), nhưng JavaScript bị giới hạn ở số nguyên 53 bit. Nếu được yêu cầu, đầu ra 64 bit đầy đủ vẫn có thể được sử dụng bằng cách thay đổi dòng trả về cho chuỗi hoặc mảng hex.

Xin lưu ý rằng việc xây dựng các chuỗi hex có thể làm chậm đáng kể quá trình xử lý hàng loạt trong các tình huống quan trọng về hiệu năng.

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

Và chỉ để giải trí, đây là hàm băm tối thiểu 32 bit trong 89 ký tự với chất lượng cao hơn cả FNV hoặc 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}

4
Ồ, cái này tốt hơn nhiều so với cái đầu tiên thông thường * 31 cho các đầu vào ngắn (hoặc tương tự). :)
lapo

2
Trường hợp nào được chkhởi tạo?
hellowill89

3
@ hellowill89 woops, tôi quên khai báo và đang chảy máu vào phạm vi toàn cầu. đã sửa ngay bây giờ, cảm ơn: ')
bryc

Không thành công cho IE 11: Object không hỗ trợ thuộc tính hoặc phương thức 'imul'.
BachT 18/03/19

2
@BachT Bạn có thể sử dụng một polyfill hoặc shim ES6 đầy đủ . Nhưng IE11 bị đóng băng một cách bi thảm vào năm 2009 mà không có bản cập nhật.
bryc

28

Nếu nó giúp được bất cứ ai, tôi đã kết hợp hai câu trả lời hàng đầu thành một phiên bản cũ hơn cho trình duyệt, sử dụng phiên bản nhanh nếu reducecó và quay lại giải pháp của esmirusha nếu không.

/**
 * @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;
}

Cách sử dụng giống như:

var hash = "some string to be hashed".hashCode();

Làm thế nào để tối ưu hóa mã này để chạy nhanh hơn trong mọi trình duyệt. String.prototype.hashCode = function(){ var hash = 5381; if (this.length === 0) return hash; for (var i = 0; i < this.length; i++) { var character = this.charCodeAt(i); hash = ((hash<<5)+hash)^character; // Convert to 32bit integer } return hash; }
Musakkhir Sayyed

26

Đây là một biến thể tinh chế và hoạt động tốt hơn:

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;
};

Điều này phù hợp với việc triển khai tiêu chuẩn của Java object.hashCode()

Đây cũng là một trong đó chỉ trả về mã băm tích cực:

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

Và đây là một kết hợp phù hợp với Java chỉ trả về mã băm tích cực:

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

Thưởng thức!


2
câu trả lời tuyệt vời, nhưng mục đích của << 0 là gì?
koolaang

8
@koolaang đó là toán tử shit trái, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
mmm

29
@momomo Ý bạn là dịch chuyển trái ?
wdh

2
@momomo Tôi nghĩ rằng anh ấy đã hỏi tại sao đó là sự dịch chuyển trái của bit 0.
jpfx1342

3
@Maykonn (2 ^ 32 - 1)
Nijraj Gelani

24

Tôi hơi ngạc nhiên khi chưa có ai nói về API SubicateCrypto mới .

Để có được một hàm băm từ một chuỗi, bạn có thể sử dụng subtle.digestphương thức:

function getHash(str, algo = "SHA-256") {
  let strBuf = new TextEncoder('utf-8').encode(str);
  return crypto.subtle.digest(algo, strBuf)
    .then(hash => {
      window.hash = hash;
      // here hash is an arrayBuffer, 
      // so we'll connvert it to its hex version
      let result = '';
      const view = new DataView(hash);
      for (let i = 0; i < hash.byteLength; i += 4) {
        result += ('00000000' + view.getUint32(i).toString(16)).slice(-8);
      }
      return result;
    });
}

getHash('hello world')
  .then(hash => {
    console.log(hash);
  });


4
Tôi đồng ý. Việc chuyển đổi thành hex có thể được thực hiện khác một chút ...var promise = crypto.subtle.digest({name: "SHA-256"}, Uint8Array.from(data)); promise.then(function(result){ console.log(Array.prototype.map.call(new Uint8Array(result), x => x.toString(16).padStart(2, '0')).join('')); });
Denis Giffeler

3
Hàm băm mật mã cho chuỗi là hơi quá mức .. cryptokhông chính xác là hiệu suất.
bryc

Chất lượng đáng tin cậy ngẫu nhiên mà không phải phụ thuộc vào người chạy thử nghiệm, tích hợp (không cần triển khai tùy chỉnh), có thể tạo hạt giống và tôi chỉ cần vài trăm số để tạo bản đồ trò chơi, điều này có vẻ hoàn hảo. Nhưng hóa ra hoàn toàn không có cách nào để làm điều đó một cách đồng bộ. Việc phải cung cấp một số cuộc gọi lại không đồng bộ mỗi khi bạn gọi công cụ ngẫu nhiên được tạo mầm của mình làm cho mã siêu khó đọc và trông thật lố bịch. Tôi không biết ai đã đưa ra giao diện crypto.subussy tinh vi này, vì vậy cuối cùng tôi đã phải dùng xmur3 + sfc32 từ câu trả lời này: stackoverflow.com/a/47593316/1201863
Luc

7

Nhờ ví dụ của mar10, tôi đã tìm ra cách để có được kết quả tương tự trong C # VÀ Javascript cho FNV-1a. Nếu ký tự unicode có mặt, phần trên bị loại bỏ vì lợi ích của hiệu suất. Không biết tại sao sẽ hữu ích khi duy trì những thứ đó khi băm, vì hiện tại chỉ băm các đường dẫn url.

Phiên bản 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());
}

Phiên bản 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);
}

@mathiasrw Các ký tự Unicode có thể vượt quá 8 bit trong bộ nhớ, vì vậy tôi giả sử 0xFF chỉ đơn giản che giấu mọi thứ bên ngoài phạm vi đó. Xem thêm về charCodeAt () tại đây: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Kẻ

Nếu ES6 có sẵn (tất cả các động cơ hiện đại hỗ trợ nó), Math.imulcó thể được sử dụng cho bước nhân, giúp cải thiện đáng kể hiệu suất . Chỉ vấn đề là, nó sẽ không làm việc trong IE11 mà không có một shim .
bryc

6

Một cách nhanh chóng và súc tích được điều chỉnh từ đây :

String.prototype.hashCode = function() {
  var hash = 5381, i = this.length
  while(i)
    hash = (hash * 33) ^ this.charCodeAt(--i)
  return hash >>> 0;
}

5

Tôi cần một chức năng tương tự (nhưng khác nhau) để tạo ID-ish duy nhất dựa trên tên người dùng và thời gian hiện tại. Vì thế:

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()

Sản xuất:

2DVFXJGEKL
6IZPAKFQFL
ORGOENVMG
... etc 

chỉnh sửa tháng 6 năm 2015: Đối với mã mới, tôi sử dụng shortid: https://www.npmjs.com/package/shortid


2
@ t0r0X bây giờ tôi sử dụng một mô-đun gọi là shortid: npmjs.com/package/shortid
jcollum

1
Làm thế nào bạn đang sử dụng tên người dùng với shortid? Nó dường như chỉ tạo id nhưng tôi không thấy cách bạn đang sử dụng để tạo băm từ một chuỗi
cyberwombat

1
Câu trả lời này có 3 downvote. Đối với cuộc sống của tôi, tôi không thể tưởng tượng tại sao. Không ai nói bất cứ điều gì ...: - /
jcollum

1
@jcollum đó là lý do tại sao tôi gần như không bao giờ trả lời các câu hỏi cũ .. đánh và chạy không được chú ý. ngay cả sau khi bạn sửa chữa câu trả lời, không ai đi cùng để cân bằng nó.
bryc

5

Một lớp lót nhanh (rất dài) của tôi dựa trên Multiply+Xorphương pháp của FNV :

my_string.split('').map(v=>v.charCodeAt(0)).reduce((a,v)=>a+((a<<7)+(a<<3))^v).toString(16);

5

SubussyCrypto.digest

Tôi không sử dụng ngôn ngữ phía máy chủ nên tôi không thể làm theo cách đó.

Bạn có chắc là bạn không thể làm theo cách đó ?

Bạn có quên bạn đang sử dụng Javascript, ngôn ngữ luôn phát triển?

Hãy thử SubtleCrypto. Nó hỗ trợ các hàm băm SHA-1, SHA-128, SHA-256 và 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.")));
})();


Điều này khác với câu trả lời của Kaiido hai năm trước bạn như thế nào?
Lục

@Luc Nó không, rõ ràng.
Toàn cảnh

3

Tôi kinda muộn để đảng, nhưng bạn có thể sử dụng mô-đun này: crypto :

const crypto = require('crypto');

const SALT = '$ome$alt';

function generateHash(pass) {
  return crypto.createHmac('sha256', SALT)
    .update(pass)
    .digest('hex');
}

Kết quả của hàm này luôn là 64chuỗi ký tự; đại loại như thế này:"aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"


2

Tôi đã kết hợp hai giải pháp (người dùng esmirusha và lordvlad) để có được một chức năng nên nhanh hơn cho các trình duyệt hỗ trợ chức năng js giảm () và vẫn tương thích với các trình duyệt cũ:

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);   
    } else {

        var hash = 0, i, chr, len;
        if (this.length == 0) return hash;
        for (i = 0, len = this.length; i < len; i++) {
        chr   = this.charCodeAt(i);
        hash  = ((hash << 5) - hash) + chr;
        hash |= 0; // Convert to 32bit integer
        }
        return hash;
    }
};

Thí dụ:

my_string = 'xyz';
my_string.hashCode();

2

Nếu bạn muốn tránh va chạm, bạn có thể muốn sử dụng hàm băm an toàn như SHA-256 . Có một số triển khai JavaScript SHA-256.

Tôi đã viết các bài kiểm tra để so sánh một số triển khai băm, xem https://github.com/brillout/test-javascript-hash-im THỰCations .

Hoặc truy cập http://brillout.github.io/test-javascript-hash-im THỰCations / , để chạy thử nghiệm.


1
Sử dụng băm mật mã an toàn có thể cực kỳ chậm. Tránh va chạm là một sản phẩm có chiều rộng bit, không bảo mật. Băm không mã hóa 128 bit hoặc thậm chí 64 bit là quá đủ cho hầu hết các mục đích. MurmurHash3_x86_128 khá nhanh và có khả năng va chạm rất thấp.
bryc

2

Đây phải là hàm băm an toàn hơn một chút so với một số câu trả lời khác, nhưng trong một hàm, không có bất kỳ nguồn tải trước nào

Về cơ bản, tôi đã tạo ra một phiên bản đơn giản hóa của sha1.
Bạn lấy các byte của chuỗi và nhóm chúng theo 4 đến 32 bit "từ"
Sau đó, chúng tôi mở rộng 8 từ thành 40 từ (để có tác động lớn hơn đến kết quả).
Điều này đi đến hàm băm (mức giảm cuối cùng) trong đó chúng ta thực hiện một số phép toán với trạng thái hiện tại và đầu vào. Chúng tôi luôn nhận được 4 từ.
Đây gần như là phiên bản một lệnh / một dòng sử dụng bản đồ, giảm ... thay vì các vòng lặp, nhưng nó vẫn khá nhanh

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("");
};

chúng tôi cũng chuyển đổi đầu ra thành hex để lấy một chuỗi thay vì mảng từ.
Cách sử dụng rất đơn giản. cho mẫu "a string".hash()sẽ trở lại"88a09e8f9cc6f8c71c4497fbb36f84cd"


1

Tôi đã thực hiện một cách ghép đơn giản các mã char được chuyển đổi thành các chuỗi hex. Điều này phục vụ một mục đích tương đối hẹp, cụ thể là chỉ cần một biểu diễn băm của chuỗi SHORT (ví dụ như tiêu đề, thẻ) để được trao đổi với phía máy chủ mà không có lý do liên quan có thể dễ dàng thực hiện cổng Java hashCode được chấp nhận. Rõ ràng không có ứng dụng bảo mật ở đây.

String.prototype.hash = function() {
  var self = this, range = Array(this.length);
  for(var i = 0; i < this.length; i++) {
    range[i] = i;
  }
  return Array.prototype.map.call(range, function(i) {
    return self.charCodeAt(i).toString(16);
  }).join('');
}

Điều này có thể được thực hiện nhanh hơn và chấp nhận trình duyệt với Underscore. Thí dụ:

"Lorem Ipsum".hash()
"4c6f72656d20497073756d"

Tôi cho rằng nếu bạn muốn băm các chuỗi lớn hơn theo cách tương tự, bạn chỉ có thể giảm mã char và hex hóa tổng kết quả thay vì nối các ký tự riêng lẻ lại với nhau:

String.prototype.hashLarge = function() {
  var self = this, range = Array(this.length);
  for(var i = 0; i < this.length; i++) {
    range[i] = i;
  }
  return Array.prototype.reduce.call(range, function(sum, i) {
    return sum + self.charCodeAt(i);
  }, 0).toString(16);
}

'One time, I hired a monkey to take notes for me in class. I would just sit back with my mind completely blank while the monkey scribbled on little pieces of paper. At the end of the week, the teacher said, "Class, I want you to write a paper using your notes." So I wrote a paper that said, "Hello! My name is Bingo! I like to climb on things! Can I have a banana? Eek, eek!" I got an F. When I told my mom about it, she said, "I told you, never trust a monkey!"'.hashLarge()
"9ce7"

Đương nhiên, nguy cơ va chạm với phương pháp này sẽ cao hơn, mặc dù bạn có thể sử dụng số học trong phần giảm nhưng bạn muốn đa dạng hóa và kéo dài hàm băm.


1

Phiên bản đơn giản hóa của câu trả lời của @ esmirusha.

Tôi không ghi đè Chuỗi trong phiên bản này, vì điều đó có thể dẫn đến một số hành vi không mong muốn.

function hashCode(str) {
    var hash = 0;
    for (var i = 0; i < str.length; i++) {
        hash = ~~(((hash << 5) - hash) + str.charCodeAt(i));
    }
    return hash;
}

1

Thêm điều này bởi vì chưa ai làm, và điều này dường như được yêu cầu và thực hiện rất nhiều với băm, nhưng nó luôn được thực hiện rất kém ...

Điều này nhận đầu vào chuỗi và số lượng tối đa bạn muốn hàm băm bằng nhau và tạo ra một số duy nhất dựa trên đầu vào chuỗi.

Bạn có thể sử dụng điều này để tạo một chỉ mục duy nhất thành một mảng hình ảnh (Nếu bạn muốn trả lại một hình đại diện cụ thể cho người dùng, được chọn ngẫu nhiên, nhưng cũng được chọn dựa trên tên của họ, vì vậy nó sẽ luôn được gán cho người có tên đó ).

Tất nhiên, bạn cũng có thể sử dụng điều này để trả về một chỉ mục thành một mảng màu, như để tạo màu nền avatar độc đáo dựa trên tên của ai đó.

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);
}

-1

Tôi không thấy bất kỳ lý do nào để sử dụng mã tiền điện tử quá phức tạp này thay vì các giải pháp sẵn sàng sử dụng, như thư viện băm đối tượng, v.v. dựa vào nhà cung cấp sẽ hiệu quả hơn, tiết kiệm thời gian và giảm chi phí bảo trì.

Chỉ cần sử dụng https://github.com/puleos/object-hash

var hash = require('object-hash');

hash({foo: 'bar'}) // => '67b69634f9880a282c14a0f0cb7ba20cf5d677e9'
hash([1, 2, 2.718, 3.14159]) // => '136b9b88375971dff9f1af09d7356e3e04281951'

Mã nguồn của lib đó thậm chí không thể đọc được .. chỉ 50k mã được rút gọn.
bryc

1
@bryc đó là cách mã nhà cung cấp trông như thế nào :) và đối với các nguồn bạn có thể kiểm tra github.com/puleos/object-hash/blob/master/index.js
Oleg Abrazhaev

Mã tối thiểu là 35,4 KB trong khi nguồn đầy đủ là 14,2 KB? Điều đó không có ý nghĩa.
bryc

2
@bryc bạn đã xem xét dòng này chưa? var crypto = require('crypto');. Tôi nghĩ rằng nó thêm mã phụ thuộc này từ nhà cung cấp trong phiên bản rút gọn trong quá trình xây dựng.
Oleg Abrazhaev

Nếu bạn thực sự cần băm đối tượng, tôi đã viết bất kỳ tuần tự hóa nào để tuần tự hóa BẤT K Object đối tượng nào với các khóa sắp xếp, sau đó cyrb53 để tạo băm base36.
Polv
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.