Hasilkan Hash dari string di Javascript

589

Saya perlu mengubah string ke beberapa bentuk hash. Apakah ini mungkin dalam JavaScript?

Saya tidak menggunakan bahasa sisi server jadi saya tidak bisa melakukannya dengan cara itu.

Freesnöw
sumber
7
MD5 tidak aman, jadi jangan mencari yang itu.
henrikstroem
166
@henrikstroem Tergantung pada apa yang Anda hashing; tidak ada salahnya menggunakan md5 untuk membuat hash untuk tujuan non-keamanan.
Brad Koch
7
@BradKoch Tergantung pada apa yang Anda lakukan; tidak ada salahnya menggunakan md5 untuk tujuan keamanan. Tentu ada metode yang lebih baik untuk mem-hashing kata sandi, tetapi md5 tidak masalah untuk melakukan hal-hal seperti menandatangani URL.
Paul Ferrett
81
Saya merasa lucu ketika MD5 dikritik dalam komentar di sini, hampir semua jawaban merekomendasikan algoritma hash yang jauh lebih buruk dan mendapatkan banyak upvotes.
domen
38
Menggunakan MD5 untuk memverifikasi bahwa unduhan menjadi utuh tidak secara ajaib mengirim email kata sandi Anda ke semua rekan kerja Anda.
James M. Lay

Jawaban:

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

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

esmiralha
sumber
22
Ini sama dengan yang digunakan di Jawa. Yang hash << 5 - hashsama hash * 31 + chartapi BANYAK lebih cepat. Itu bagus karena sangat cepat, dan 31 adalah prime kecil. Menang menang di sana.
corsiKa
41
Saya melakukan beberapa tes pada jsperf ( jsperf.com/hashing-strings ) dan fungsi bitwise sebenarnya lebih lambat daripada fungsi berbasis angka.
skerit
17
@PeterAronZentai Mengapa "tidak dapat digunakan"? Output yang dihasilkan oleh kode berbasis angka (hash * 31) + charidentik dengan output yang dihasilkan oleh kode berbasis shift ((hash<<5)-hash)+char, bahkan untuk string yang sangat panjang (saya sudah mengujinya dengan string yang berisi lebih dari satu juta karakter), jadi itu tidak "tidak dapat digunakan" dalam hal akurasi. Kompleksitasnya adalah O (n) untuk versi berbasis nomor dan berbasis shift, sehingga tidak "tidak dapat digunakan" dalam hal kompleksitas.
TachyonVortex
13
Adakah yang bisa mengomentari keunikan (atau tidak) dari output? Khususnya, jika saya hanya menggunakan hash ini untuk string dengan panjang kurang dari n, apa yang terbesar nyang saya tidak mungkin memiliki tabrakan?
Don McCurdy
34
Apakah ada alasan mengapa ini perlu (atau seharusnya) ada di prototipe String? Apakah kurang efektif / efisien jika hanya memiliki mis; var hashCode = function hashCode (str) {etc...}? Dan kemudian gunakan sebagai hashCode("mystring")?
rattray
146

EDIT

berdasarkan tes jsperf saya, jawaban yang diterima sebenarnya lebih cepat: http://jsperf.com/hashcodelordvlad

ASLI

jika ada yang tertarik, ini versi yang lebih baik (lebih cepat), yang akan gagal pada peramban lama yang tidak memiliki reducefungsi array.

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

versi fungsi panah satu-liner:

hashCode = s => s.split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0)
tuan
sumber
3
apakah ada cara untuk mendapatkan hash yang hanya angka positif?
Prosto Trader
46
aneh. Saya baru saja mengujinya dan ternyata lebih lambat dari jawaban yang diterima. jsperf.com/hashcodelordvlad
lordvlad
113
Orang baik @lordvlad, benar-benar menguji jawabannya sendiri, dan kemudian melaporkan ketika itu lebih lambat.
mikemaccana
9
Saya baru sadar: Sangat masuk akal bahwa jawaban yang diterima lebih cepat, karena versi saya harus mengubah string menjadi array terlebih dahulu, mengalokasikan memori baru dan menyalin setiap karakter ...
lordvlad
5
[] .reduce.call (str, (p, c, i, a) => (p << 5) - p + a.charCodeAt (i), 0);
Dizzy
108

Catatan: Bahkan dengan hash 32-bit terbaik, tabrakan akan terjadi cepat atau lambat.

Kemungkinan tabrakan hash dapat dihitung sebagai 1 - e ^ (-k (k-1) / 2N, diperkirakan sebagai k ^ 2 / 2N ( lihat di sini ). Ini mungkin lebih tinggi dari intuisi yang disarankan:
Dengan asumsi hash 32-bit dan k = 10.000 item, tabrakan akan terjadi dengan kemungkinan 1,2%. Untuk 77.163 sampel, probabilitas menjadi 50%! ( kalkulator ).
Saya menyarankan solusi di bagian bawah.

Dalam jawaban atas pertanyaan ini Algoritma hashing mana yang paling baik untuk keunikan dan kecepatan? , Ian Boyd memposting analisis mendalam yang baik . Singkatnya (seperti yang saya tafsirkan), ia sampai pada kesimpulan bahwa Murmur adalah yang terbaik, diikuti oleh FNV-1a.
Algoritma String.hashCode () Java yang diusulkan esmiralha tampaknya merupakan varian dari DJB2.

  • FNV-1a memiliki distribusi yang lebih baik daripada DJB2, tetapi lebih lambat
  • DJB2 lebih cepat dari FNV-1a, tetapi cenderung menghasilkan lebih banyak tabrakan
  • MurmurHash3 lebih baik dan lebih cepat daripada DJB2 dan FNV-1a (tetapi implementasi yang dioptimalkan membutuhkan lebih banyak baris kode daripada FNV dan DJB2)

Beberapa tolok ukur dengan string input besar di sini: http://jsperf.com/32-bit-hash
Ketika string input pendek di -hash, kinerja murmur turun, relatif terhadap DJ2B dan FNV-1a: http://jsperf.com/32- bit-hash / 3

Jadi secara umum saya akan merekomendasikan murmur3.
Lihat di sini untuk implementasi JavaScript: https://github.com/garycourt/murmurhash-js

Jika string input pendek dan kinerja lebih penting daripada kualitas distribusi, gunakan DJB2 (seperti yang diusulkan oleh jawaban yang diterima oleh esmiralha).

Jika kualitas dan ukuran kode kecil lebih penting daripada kecepatan, saya menggunakan implementasi FNV-1a ini (berdasarkan kode ini ).

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

Meningkatkan Probabilitas Tabrakan

Seperti yang dijelaskan di sini , kita dapat memperluas ukuran bit hash menggunakan trik ini:

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

Gunakan dengan hati-hati dan jangan berharap terlalu banyak.

mar10
sumber
Mengapa Anda lakukan ("0000000" + (hval >>> 0).toString(16)).substr(-8);? Bukankah itu sama dengan (hval >>> 0).toString(16)?
Manuel Meurer
3
ini menambahkan memimpin '0 sehingga hash yang dihasilkan selalu 8 karakter. Lebih mudah dibaca dan dikenali dalam keluaran, tapi itu pendapat pribadi saya
Maret
Ah ok, saya mengerti. Untuk yang kecil hval, (hval >>> 0).toString(16)mungkin kurang dari 8 karakter, jadi Anda membalutnya dengan nol. Saya hanya bingung karena (hval >>> 0).toString(16)selalu menghasilkan string karakter 8 persis untuk saya.
Manuel Meurer
3
Saya suka jawaban ini karena menghasilkan hash terdistribusi jauh lebih baik: fungsi lain yang diusulkan di sini akan membuat nilai hash konsekuen. Misalnya `hash (" example1 ") - hash (" example2 ") == 1", sementara yang ini jauh lebih tidak terduga.
GavinoGrifoni
1
Menanggapi "FNV-1a memiliki distribusi yang lebih baik daripada DJB2, tetapi lebih lambat" - Saya pikir harus dikatakan bahwa FNV1a bisa sangat cepat ketika diimplementasikan menggunakan Math.imulfungsi ES6 . Itu saja membuatnya menjadi tolok ukur teratas, dan akhirnya menjadi pilihan yang lebih baik daripada DJB2 dalam jangka panjang.
bryc
64

Berdasarkan jawaban yang diterima di ES6. Lebih kecil, dapat dikelola, dan berfungsi di browser modern.

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

versi fungsi panah satu-liner:

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

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

deekshith
sumber
1
Terima kasih telah berbagi, saya menambahkan str += ""sebelum hashing untuk menghindari pengecualian yang str.split is not a functiondilemparkan ketika non-string dilewatkan sebagai parameter
BeetleJuice
4
Tetapi jauh lebih lambat dari semua ini: https://jsperf.com/hashing-strings
AndyO
Saya juga baru memperhatikan bahwa solusi "retro" tercepat sebenarnya lebih kecil juga jika Anda menghapus feed garis sehingga hanya 3 baris.
AndyO
2
Adakah cara agar ini menghasilkan hanya hasil positif tetapi masih unik?
Dids
3
@deekshith Jawaban yang diterima digunakan hash |= 0untuk mengonversi ke int 32 bit. Implementasi ini tidak. Apakah ini bug?
Sukima
48

Hampir separuh jawabannya adalah implementasi dari Java String.hashCode, yang tidak berkualitas tinggi atau super cepat. Tidak ada yang terlalu istimewa, itu hanya kelipatan 31 untuk setiap karakter. Ini dapat diimplementasikan secara sederhana dan efisien dalam satu baris, dan jauh lebih cepat dengan Math.imul:

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

Dengan itu, ada sesuatu yang lebih baik— cyrb53 , hash 53-bit sederhana namun berkualitas tinggi. Ini cukup cepat, menyediakan distribusi hash yang sangat baik, dan memiliki tingkat tabrakan lebih rendah secara signifikan dibandingkan dengan setiap 32-bit hash.

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

Mirip dengan algoritma MurmurHash / xxHash yang terkenal, ia menggunakan kombinasi multiplikasi dan Xorshift untuk menghasilkan hash, tetapi tidak . Akibatnya lebih cepat daripada dalam JavaScript dan secara signifikan lebih mudah untuk diterapkan.

Ini mencapai longsoran salju (tidak ketat), yang pada dasarnya berarti perubahan kecil pada input memiliki perubahan besar dalam output, membuat hash yang dihasilkan tampak acak:

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

Anda juga dapat menyediakan seed untuk stream alternatif dari input yang sama:

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

Secara teknis ini adalah hash 64-bit (dua hash 32-bit yang tidak berkorelasi secara paralel), tetapi JavaScript terbatas pada bilangan bulat 53-bit. Jika diperlukan, output 64-bit penuh masih dapat digunakan dengan mengubah garis balik untuk string hex atau array.

Ketahuilah bahwa membangun string hex dapat secara drastis memperlambat pemrosesan batch dalam situasi yang kritis terhadap kinerja.

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

Dan hanya untuk bersenang-senang, inilah hash minimal 32-bit dalam 89 karakter dengan kualitas lebih tinggi daripada FNV atau 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}
bryc
sumber
4
Wow, ini jauh lebih baik daripada yang biasa * 31 untuk input pendek (atau serupa). :)
lapo
2
Di mana chdiinisialisasi?
hellowill89
3
@ hellowill89 woops, saya lupa mendeklarasikannya dan berdarah ke ruang lingkup global. diperbaiki sekarang, terima kasih: ')
bryc
Gagal untuk IE 11: Objek tidak mendukung properti atau metode 'imul'.
BachT
2
@ BachT Anda dapat menggunakan polyfill atau ES6 shim penuh . Tetapi IE11 secara tragis dibekukan pada tahun 2009 tanpa pembaruan.
bryc
28

Jika itu membantu siapa pun, saya menggabungkan dua jawaban teratas ke dalam versi yang lebih lama dengan browser-toleran, yang menggunakan versi cepat jika reducetersedia dan kembali ke solusi esmiralha jika tidak.

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

Penggunaannya seperti:

var hash = "some string to be hashed".hashCode();
Kyle Falconer
sumber
cara mengoptimalkan kode ini agar berjalan lebih cepat di setiap browser. 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

Ini adalah varian yang disempurnakan dan berkinerja lebih baik:

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

Ini sesuai dengan implementasi standar Java object.hashCode()

Ini juga salah satu yang hanya mengembalikan kode hash positif:

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

Dan di sini ada pencocokan untuk Java yang hanya mengembalikan kode hash positif:

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

Nikmati!

mmm
sumber
2
jawaban yang bagus, tetapi apa tujuan dari << 0?
koolaang
8
@koolaang itu adalah operator omong kosong kiri, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mmm
29
@momomo Apakah maksud Anda shift kiri ?
wdh
2
@ momomo Saya pikir dia bertanya mengapa itu adalah pergeseran kiri nol bit.
jpfx1342
3
@Maykonn (2 ^ 32 - 1)
Nijraj Gelani
24

Saya agak terkejut tidak ada yang berbicara tentang API SubtleCrypto baru .

Untuk mendapatkan hash dari string, Anda dapat menggunakan subtle.digestmetode ini:

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

Kaiido
sumber
4
Saya setuju. Konversi ke hex dapat dilakukan sedikit berbeda ...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
Fungsi hash kriptografis untuk string sedikit berlebihan .. cryptotidak benar-benar performant.
bryc
Kualitas acak yang dapat diandalkan tanpa harus bergantung pada orang yang menjalankan tes, built-in (tidak perlu implementasi khusus), dapat ditaburkan, dan saya hanya membutuhkan beberapa ratus angka untuk membuat peta game, ini tampak sempurna. Tetapi ternyata sama sekali tidak ada cara untuk melakukannya secara serempak. Harus menyediakan beberapa panggilan balik async setiap kali Anda memanggil mesin acak unggulan Anda membuat kode super tidak dapat dibaca dan terlihat konyol. Saya tidak mengerti siapa yang muncul dengan antarmuka crypto.sappy yang jelek ini, jadi pada akhirnya saya harus menggunakan xmur3 + sfc32 dari jawaban ini: stackoverflow.com/a/47593316/1201863
Luc
7

Berkat contoh oleh mar10, saya menemukan cara untuk mendapatkan hasil yang sama dalam C # DAN Javascript untuk FNV-1a. Jika ada unicode chars, bagian atas dibuang untuk kinerja. Tidak tahu mengapa mempertahankannya saat hashing, karena saya hanya mem-path path url untuk saat ini.

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

Versi 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);
}
djabraham
sumber
@mathiasrw Dimungkinkan untuk karakter Unicode melebihi 8 bit dalam memori, jadi saya menganggap 0xFF hanya menutupi apa pun di luar rentang itu. Lihat lebih lanjut tentang charCodeAt () di sini: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
djabraham
Jika ES6 tersedia (semua mesin modern mendukungnya), Math.imuldapat digunakan untuk langkah multiplikasi, yang sangat meningkatkan kinerja . Satu-satunya masalah adalah, itu tidak akan berfungsi di IE11 tanpa shim .
bryc
6

Yang cepat dan ringkas yang diadaptasi dari sini :

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

Saya membutuhkan fungsi serupa (tetapi berbeda) untuk menghasilkan ID unik-ish berdasarkan nama pengguna dan waktu saat ini. Begitu:

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

Menghasilkan:

2DVFXJGEKL
6IZPAKFQFL
ORGOENVMG
... etc 

sunting Jun 2015: Untuk kode baru saya menggunakan shortid: https://www.npmjs.com/package/shortid

jcollum
sumber
2
@ t0r0X nah sekarang saya menggunakan modul bernama shortid: npmjs.com/package/shortid
jcollum
1
Bagaimana Anda menggunakan nama pengguna dengan shortid? Sepertinya menghasilkan id tetapi saya tidak melihat bagaimana Anda menggunakan untuk menghasilkan hash dari string
cyberwombat
1
Jawaban ini memiliki 3 downvotes. Untuk kehidupan saya, saya tidak bisa membayangkan mengapa. Tidak ada yang mengatakan apa-apa ...: - /
jcollum
1
@ jcollum itu sebabnya saya hampir tidak pernah menjawab pertanyaan basi .. tekan dan jalankan pergi tanpa diketahui. bahkan setelah Anda memperbaiki jawabannya, tidak ada yang datang untuk menyeimbangkannya.
bryc
5

Satu liner cepat (sangat panjang) saya berdasarkan Multiply+Xormetode FNV :

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

SubtleCrypto.digest

Saya tidak menggunakan bahasa sisi server jadi saya tidak bisa melakukannya dengan cara itu.

Apakah Anda yakin tidak bisa melakukannya dengan cara itu ?

Apakah Anda lupa Anda menggunakan Javascript, bahasa yang terus berkembang?

Coba SubtleCrypto. Ini mendukung fungsi hash SHA-1, SHA-128, SHA-256, dan 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.")));
})();

Константин Ван
sumber
Apa bedanya dengan jawaban Kaiido dua tahun sebelum jawaban Anda ?
Luc
@ Luc Tidak, rupanya.
Константин Ван
3

Saya agak terlambat ke pesta, tetapi Anda dapat menggunakan modul ini: crypto :

const crypto = require('crypto');

const SALT = '$ome$alt';

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

Hasil dari fungsi ini selalu adalah 64karakter string; sesuatu seperti ini:"aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"

Ariel Jiménez
sumber
2

Saya telah menggabungkan dua solusi (pengguna esmiralha dan lordvlad) untuk mendapatkan fungsi yang seharusnya lebih cepat untuk browser yang mendukung pengurangan fungsi js () dan masih kompatibel dengan browser lama:

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

Contoh:

my_string = 'xyz';
my_string.hashCode();
jujur
sumber
2

Jika Anda ingin menghindari tabrakan, Anda mungkin ingin menggunakan hash yang aman seperti SHA-256 . Ada beberapa implementasi JavaScript SHA-256.

Saya menulis tes untuk membandingkan beberapa implementasi hash, lihat https://github.com/brillout/test-javascript-hash-implementations .

Atau buka http://brillout.github.io/test-javascript-hash-implementations/ , untuk menjalankan tes.

brillout
sumber
1
Menggunakan hash kriptografi yang aman bisa sangat lambat. Menghindari tabrakan adalah produk dari lebar bit, bukan keamanan. Hash non-cryptographic 128 bit atau bahkan 64 bit harus lebih dari cukup untuk sebagian besar keperluan. MurmurHash3_x86_128 cukup cepat dan memiliki kemungkinan tabrakan yang sangat rendah.
bryc
2

Ini harus sedikit lebih aman daripada beberapa jawaban lain, tetapi dalam suatu fungsi, tanpa sumber yang dimuat sebelumnya

Saya pada dasarnya membuat versi sederhana sha1 yang diperkecil.
Anda mengambil byte dari string dan mengelompokkannya dengan "kata-kata" 4 sampai 32bit
Kemudian kami memperluas setiap 8 kata menjadi 40 kata (untuk dampak yang lebih besar pada hasilnya).
Ini pergi ke fungsi hashing (pengurangan terakhir) di mana kita melakukan beberapa matematika dengan keadaan saat ini dan input. Kami selalu mengeluarkan 4 kata.
Ini hampir versi satu-perintah / satu-baris menggunakan peta, kurangi ... bukan loop, tapi masih cukup cepat

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

kami juga mengonversi output menjadi hex untuk mendapatkan string, bukan array kata.
Penggunaannya sederhana. untuk expample "a string".hash()akan kembali"88a09e8f9cc6f8c71c4497fbb36f84cd"

Franartur Čech
sumber
1

Aku pergi untuk rangkaian kode char sederhana dikonversi ke string hex. Ini melayani tujuan yang relatif sempit, yaitu hanya membutuhkan representasi hash dari string PENDEK (misalnya judul, tag) untuk dipertukarkan dengan sisi server yang karena alasan yang tidak relevan tidak dapat dengan mudah mengimplementasikan hashCode Java port yang diterima. Jelas tidak ada aplikasi keamanan di sini.

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

Ini dapat dibuat lebih singkat dan toleran terhadap browser dengan Underscore. Contoh:

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

Saya kira jika Anda ingin memiliki string yang lebih besar dengan cara yang sama Anda bisa mengurangi kode char dan hexify jumlah yang dihasilkan daripada menggabungkan karakter individu bersama-sama:

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"

Tentu saja lebih banyak risiko tabrakan dengan metode ini, meskipun Anda bisa bermain-main dengan aritmatika dalam pengurangan namun Anda ingin melakukan diversifikasi dan memperpanjang hash.

swornabent
sumber
1

Jawaban @ esmiralha versi sedikit disederhanakan.

Saya tidak mengganti String dalam versi ini, karena itu bisa mengakibatkan beberapa perilaku yang tidak diinginkan.

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

Menambahkan ini karena belum ada yang melakukannya, dan ini sepertinya banyak diminta dan diimplementasikan dengan hash, tetapi selalu dilakukan dengan sangat buruk ...

Ini membutuhkan input string, dan jumlah maksimum yang Anda inginkan sama dengan hash, dan menghasilkan angka unik berdasarkan input string.

Anda dapat menggunakan ini untuk menghasilkan indeks unik ke dalam array gambar (Jika Anda ingin mengembalikan avatar tertentu untuk pengguna, yang dipilih secara acak, tetapi juga dipilih berdasarkan namanya, maka itu akan selalu diberikan kepada seseorang dengan nama itu. ).

Anda juga dapat menggunakan ini, tentu saja, untuk mengembalikan indeks ke berbagai warna, seperti untuk menghasilkan warna latar belakang avatar unik berdasarkan nama seseorang.

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);
}
Nick Steele
sumber
-1

Saya tidak melihat alasan untuk menggunakan kode kripto yang terlalu rumit ini alih-alih solusi siap pakai, seperti pustaka objek-hash, atau lain-lain yang mengandalkan vendor lebih produktif, menghemat waktu dan mengurangi biaya perawatan.

Gunakan saja https://github.com/puleos/object-hash

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

hash({foo: 'bar'}) // => '67b69634f9880a282c14a0f0cb7ba20cf5d677e9'
hash([1, 2, 2.718, 3.14159]) // => '136b9b88375971dff9f1af09d7356e3e04281951'
Oleg Abrazhaev
sumber
Kode sumber lib itu bahkan tidak dapat dibaca .. hanya 50k dari kode yang diperkecil.
bryc
1
@ bryc, kode vendor seharusnya seperti ini :) dan untuk sumber Anda dapat memeriksa github.com/puleos/object-hash/blob/master/index.js
Oleg Abrazhaev
Kode yang diperkecil adalah 35,4 KB sedangkan sumber lengkapnya adalah 14,2 KB? Itu tidak masuk akal.
bryc
2
@ bryc apakah Anda sudah mempertimbangkan baris ini? var crypto = require('crypto');. Saya pikir itu menambahkan kode dependensi ini dari vendor dalam versi yang diperkecil saat membangun.
Oleg Abrazhaev
Jika Anda benar-benar perlu meng- hash Objects, saya menulis any-serialize untuk meng -serialisasi Obyek APA dengan tombol sortir, kemudian cyrb53 untuk menghasilkan hash base36.
Polv