Javascript'te dizeden bir Hash üret


585

Dizeleri bir tür karma dönüştürmek gerekiyor. Bu JavaScript'te mümkün mü?

Sunucu tarafı bir dil kullanmıyorum, bu yüzden bu şekilde yapamam.


7
MD5 güvenli değil, bu yüzden aramayın.
henrikstroem

166
@henrikstroem Ne hash yaptığınıza bağlıdır; md5'i güvenlik dışı amaçlarla karma yapmak için yanlış bir şey yoktur.
Brad Koch

7
@BradKoch Ne yaptığınıza bağlıdır; md5'i güvenlik amacıyla kullanmakta yanlış bir şey yoktur. Parolaları karıştırmak için kesinlikle daha iyi yöntemler vardır, ancak md5, bir URL imzalamak gibi şeyler yapmak için iyidir.
Paul Ferrett

81
MD5'in burada yorumlarda eleştirilmesine rağmen, hemen hemen tüm cevapların çok daha kötü karma algoritmalar önerdiğini ve çok sayıda upvotes aldığını komik buluyorum.
domen

38
Bir yüklemenin sağlam olduğunu doğrulamak için MD5'i kullanmak, şifrelerinizi sihirli bir şekilde tüm iş arkadaşlarınıza e-postayla göndermeyecektir.
James

Yanıtlar:


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

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


22
Bu Java'da kullanılan ile aynıdır. Bu hash << 5 - hashaynı hash * 31 + charama çok daha hızlı. Güzel çünkü çok hızlı ve 31 küçük bir başbakan. Win orada kazan.
corsiKa

41
Jsperf ( jsperf.com/hashing-strings ) üzerinde birkaç test yaptım ve bitsel fonksiyon aslında sayı tabanlı fonksiyondan daha yavaş.
skerit

17
@PeterAronZentai Neden "kullanılamaz"? Sayı tabanlı kod (hash * 31) + chartarafından üretilen çıktı ((hash<<5)-hash)+char, çok uzun dizeler için bile (bir milyondan fazla karakter içeren dizelerle test ettim), vardiya tabanlı kod tarafından üretilen çıktı ile aynıdır , bu yüzden açısından "kullanılamaz" değildir doğruluk. Karmaşıklık hem sayı tabanlı hem de vardiya tabanlı sürümler için O (n) 'dir, bu nedenle karmaşıklık açısından "kullanılamaz" değildir.
TachyonVortex

13
Çıktının benzersizliği (ya da olmaması) hakkında yorum yapan var mı? Özellikle, bu karmayı yalnızca uzunluğu daha az olan dizeler için kullanırsam n, nmuhtemelen bir çarpışma yapamayacağım en büyük şey nedir?
Don McCurdy

34
Bunun String prototipinde olması gereken (veya olması gereken) bir nedeni var mı? Sadece sahip olmak daha az etkili / verimli olurdu; var hashCode = function hashCode (str) {etc...}? Ve sonra olarak kullan hashCode("mystring")?
rattray

146

DÜZENLE

jsperf testlerime dayanarak, kabul edilen cevap aslında daha hızlıdır: http://jsperf.com/hashcodelordvlad

ORİJİNAL

ilgilenen varsa, burada reducedizi işlevinden yoksun eski tarayıcılarda başarısız olacak geliştirilmiş (daha hızlı) bir sürüm var .

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

tek satırlı ok işlevi sürümü:

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

3
sadece pozitif sayı olan karma elde etmenin bir yolu var mı?
Prosto Trader

46
tuhaf. Ben sadece test ve kabul cevap daha yavaş waaay olduğu ortaya çıktı. jsperf.com/hashcodelordvlad
lordvlad

113
İyi adam @lordvlad, aslında kendi cevabını test ediyor ve daha yavaş olduğunu bildiriyor.
mikemaccana

9
Sadece fark ettim: Kabul edilen cevabın daha hızlı olması mükemmel mantıklı, çünkü
sürümüm

5
[]. reduce.call (str, (p, c, i, a) => (p << 5) - p + a.charCodeAt (i), 0);
Baş

108

Not: Hatta en iyi 32 bit karma ile, çarpışmalar olacak er ya da geç ortaya çıkar.

Karma çarpışma olasılığı ( 1 - e ^ (-k (k-1) / 2Nyaklaşık olarak buraya bakınız ) şeklinde hesaplanabilir . Bu, sezginin önerdiğinden daha yüksek olabilir: 32 bit karma ve k = 10.000 öğe varsayarsak,% 1.2 olasılıkla bir çarpışma meydana gelir. 77.163 numune için olasılık% 50 olur! ( hesap makinesi ). En altta bir çözüm öneririm.k ^ 2 / 2N

Bu sorunun cevabında Eşitlik ve hız için hangi karma algoritma en iyisidir? , Ian Boyd iyi bir derinlik analizi yayınladı . Kısacası (yorumladığım gibi), Murmur'un en iyisi olduğu sonucuna varır ve onu FNV-1a izler.
Esmiralha'nın önerdiği Java'nın String.hashCode () algoritması DJB2'nin bir varyantı gibi görünüyor.

  • FNV-1a'nın DJB2'den daha iyi bir dağılımı var, ancak daha yavaş
  • DJB2, FNV-1a'dan daha hızlıdır, ancak daha fazla çarpışma eğilimi gösterir
  • MurmurHash3, DJB2 ve FNV-1a'dan daha iyi ve daha hızlıdır (ancak optimize edilmiş uygulama FNV ve DJB2'den daha fazla kod satırı gerektirir)

Burada büyük giriş dizeleri ile Bazı kriterler: http://jsperf.com/32-bit-hash
zaman kısa giriş dizeleri Karma yapılmadan, DJ2B ve FNV-1a göre Üfürüm performansını damla: http://jsperf.com/32- bit-karma / 3

Yani genel olarak üfürüm3 tavsiye ederim.
JavaScript uygulaması için buraya bakın: https://github.com/garycourt/murmurhash-js

Giriş dizeleri kısaysa ve performans dağıtım kalitesinden daha önemliyse, DJB2 kullanın (esmiralha tarafından kabul edilen cevapta önerildiği gibi).

Kalite ve küçük kod boyutu hızdan daha önemliyse, bu FNV-1a uygulamasını kullanıyorum ( bu koda dayanarak ).

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

Çarpışma Olasılığını Artırın

Burada açıklandığı gibi , bu hileyi kullanarak karma bit boyutunu genişletebiliriz:

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

Dikkatle kullanın ve çok fazla beklemeyin.


Neden yapıyorsun ("0000000" + (hval >>> 0).toString(16)).substr(-8);? Bu aynı değil (hval >>> 0).toString(16)mi?
Manuel Meurer

3
sonuçtaki karma her zaman 8 karakter uzunluğunda olur. Çıktıları okumak ve tanımak daha kolay, ama bu benim kişisel görüşüm
mar10

Ah tamam, anladım. Küçük için hval, (hval >>> 0).toString(16)sıfırlarla size böylece ped o az 8 karakterden olabilir. Sadece kafam karıştı çünkü (hval >>> 0).toString(16)her zaman tam olarak 8 karakter dizisi elde ettim.
Manuel Meurer

3
Bu cevabı çok seviyorum çünkü çok daha iyi dağıtılmış bir karma üretiyor: burada önerilen diğer işlevler sonuçta karma değerlerini yapacak. Örneğin, `` hash ("example1") - hash ("example2") == 1 ", bu çok daha tahmin edilemezken
GavinoGrifoni

1
"FNV-1a, DJB2'den daha iyi bir dağılıma sahiptir, ancak daha yavaştır" - Bence ES6 Math.imulfonksiyonu kullanılarak uygulandığında FNV1a'nın son derece hızlı olabileceği söylenmelidir . Sadece bu onu en iyi kriterler haline getiriyor ve sonuçta uzun vadede DJB2'den daha iyi bir seçim.
bryc

64

ES6'daki kabul edilen cevaba dayanarak . Daha küçük, bakımı kolay ve modern tarayıcılarda çalışır.

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

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

DÜZENLEME (2019-11-04) :

tek satırlı ok işlevi sürümü:

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

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


1
Paylaşım için teşekkürler Dizeler olmayan parametreler geçirildiğinde str += ""istisna önlemek için karma önce ekledistr.split is not a function
BeetleJuice

4
Ancak bunlardan çok daha yavaş: https://jsperf.com/hashing-strings
AndyO

Ayrıca, hat beslemelerini yalnızca 3 satır uzunluğunda olacak şekilde kaldırırsanız, en hızlı "retro" çözümün aslında daha küçük olduğunu fark ettim.
AndyO

2
Bu sadece olumlu ama yine de benzersiz sonuçlar üretmek için herhangi bir yolu var mı?
Dids

3
@deekshith Kabul edilen cevap, hash |= 032 bit int değerine dönüştürmek için kullanılır. Bu uygulama değil. Bu bir hata mı?
Sukima

48

Cevapların neredeyse yarısı String.hashCode, ne yüksek kalite ne de süper hızlı olan Java uygulamalarıdır . Çok özel bir şey değil, her karakter için sadece 31 ile çarpar. Tek bir hatta basit ve verimli bir şekilde uygulanabilir ve aşağıdakilerle çok daha hızlıdır Math.imul:

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

Bu şekilde, daha iyi bir şey var - cyrb53 , basit ama kaliteli 53 bitlik bir karma. Bu, oldukça hızlı çok iyi karma dağılımını sağlar ve kıyasla önemli ölçüde düşük çarpışma oranları herhangi 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);
};

İyi bilinen MurmurHash / xxHash algoritmalarına benzer şekilde, karmayı oluşturmak için çarpma ve Xorshift kombinasyonunu kullanır , ancak tam olarak değil. Sonuç olarak, JavaScript'ten daha hızlıdır ve uygulanması oldukça kolaydır.

Çığ (katı olmayan) elde eder, bu da girişteki küçük değişikliklerin çıktıda büyük değişikliklere sahip olması ve sonuçta ortaya çıkan karmaın rastgele görünmesini sağlar:

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

Aynı girdinin alternatif akışları için bir tohum da sağlayabilirsiniz:

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

Teknik olarak 64 bitlik bir karma (paralel olarak iki adet ilişkisiz 32 bit karma), ancak JavaScript 53 bitlik tamsayılarla sınırlıdır. Gerekirse, tam 64 bit çıktı, onaltılı bir dize veya dizi için dönüş çizgisini değiştirerek kullanılabilir .

Onaltılı dizeler oluşturmanın performans açısından kritik durumlarda toplu işlemeyi önemli ölçüde yavaşlatabileceğini unutmayın.

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

Ve sadece eğlence için, 89 karakterde FNV veya DJB2'den bile daha yüksek kalitede minimum 32 bitlik bir karma :

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
Vay canına, bu kısa (ya da benzeri) girişler için olağan * 31 olandan çok daha iyi. :)
lapo

2
Nerede chbaşlatılır?
hellowill89

3
@ hellowill89 woops, beyan etmeyi unuttum ve küresel kapsamda kanıyordu. şimdi düzeltildi, teşekkürler: ')
bryc

IE 11 için başarısız oldu: Nesne özelliği veya yöntemi desteklemiyor 'imul'.
BachT

2
@BachT Bir kullanabilir polyfill veya tam ES6 şimi . Ancak IE11 2009'da trajik bir şekilde güncellenmedi.
bryc

28

Eğer herkese yardımcı olursa, ilk iki yanıtı, varsa hızlı sürümü kullanan ve yoksa reduceesmiralha'nın çözümüne geri dönen daha eski bir tarayıcı toleranslı sürüme birleştirdim.

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

Kullanımı gibi:

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

bu kod her tarayıcıda daha hızlı çalışacak şekilde nasıl optimize edilir. 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

Bu rafine ve daha iyi performans gösteren bir varyanttır:

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

Bu, Java'nın standart uygulamasına uyuyor object.hashCode()

Ayrıca, yalnızca pozitif hashcode'ları döndüren bir de:

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

Java için sadece pozitif hashcode'ları döndüren eşleşen bir tane:

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

Zevk almak!


2
harika bir cevap, ama << 0'ın amacı nedir?
koolaang

8
@ koolaang bu sol bok operatörü, geliştirici.mozilla.org/
mmm

29
@momomo sol kastettiniz vardiya ?
wdh

2
@momomo Sanırım neden sıfır bitlik bir sola kayma olduğunu soruyordu.
jpfx1342

3
Maykonn (2 ^
32-1

24

Kimse henüz yeni SubtleCrypto API hakkında konuşmadı biraz şaşırdım .

Bir dizeden bir karma almak için, subtle.digestyöntemi kullanabilirsiniz :

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
Katılıyorum. Hex dönüşümü biraz farklı yapılabilir ...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
Dizeler için kriptografik bir karma işlevi biraz fazladır .. cryptotam olarak performans göstermez.
bryc

Testleri çalıştıran insanlara güvenmek zorunda kalmadan güvenilir kalite rastgele, yerleşik (özel uygulama gerekmez), görülebilir ve bir oyun haritası oluşturmak için sadece birkaç yüz numaraya ihtiyacım vardı, bu mükemmel görünüyordu. Ancak, eşzamanlı olarak yapmanın kesinlikle bir yolu olmadığı ortaya çıkıyor. Tohumlu rastgele motorunuzu her aradığınızda bazı asenkron geri arama şeyleri sağlamak zorunda kalmanız, kodu süper okunamaz hale getirir ve saçma görünür. Kim bu crappy crypto.subtle arayüzü ile geldi alamadım, bu yüzden sonunda bu cevaptan xmur3 + sfc32 ile gitmek zorunda kaldı: stackoverflow.com/a/47593316/1201863
Luc

7

Mar10 örneği sayesinde, aynı sonuçları bir FNV-1a için C # AND Javascript'te elde etmenin bir yolunu buldum. Unicode karakterleri varsa, üst kısım performans uğruna atılır. Şimdilik sadece URL yollarını hash ettiğim için, hash yaparken bunları korumanın neden yararlı olacağını bilmiyorum.

C # Sürümü

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 Sürümü

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 Unicode karakterlerin bellekte 8 biti aşması mümkündür, bu yüzden 0xFF'ın bu aralığın dışındaki herhangi bir şeyi maskelediğini varsayıyorum. CharCodeAt () ile ilgili daha fazla bilgiyi burada bulabilirsiniz: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
djabraham

ES6 mevcutsa (tüm modern motorlar bunu destekler), performansı büyük ölçüde artıranMath.imul çarpma adımı için kullanılabilir . Tek sorun şim olmadan IE11'de çalışmayacak .
bryc

6

Buradan uyarlanan hızlı ve özlü :

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

5

Ben kullanıcı adı ve şimdiki zaman dayalı benzersiz bir ish ID oluşturmak için benzer bir işlev (ama farklı) gerekiyordu. Yani:

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

üretir:

2DVFXJGEKL
6IZPAKFQFL
ORGOENVMG
... etc 

düzenlemek Haziran 2015: Yeni kod için ben shortid kullanıyorum: https://www.npmjs.com/package/shortid


2
@ t0r0X Peki şimdi shortid adlı bir modül kullanıyorum: npmjs.com/package/shortid
jcollum

1
Kullanıcı adını kısa mesajla nasıl kullanıyorsunuz? Sadece ids üretmek gibi görünüyor ama bir dizeden bir karma oluşturmak için nasıl kullandığınızı görmüyorum
cyberwombat

1
Bu cevapta 3 downvotes var. Hayatım boyunca neden olduğunu hayal bile edemiyorum. Kimse bir şey söylemedi ...: - /
jcollum

1
@jcollum bu yüzden neredeyse hiç eski soruları cevaplamak .. vurmak ve çalışır fark edilmeden gitmek. cevabı düzelttikten sonra bile, kimse dengelemek için gelmez.
bryc

5

FNV Multiply+Xoryöntemine dayanan hızlı (çok uzun) bir astarım :

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

5

SubtleCrypto.digest

Sunucu tarafı bir dil kullanmıyorum, bu yüzden bu şekilde yapamam.

Bu şekilde yapamayacağınızdan emin misiniz ?

Sürekli gelişen dil olan Javascript kullandığınızı unuttunuz mu?

Deneyin SubtleCrypto. SHA-1, SHA-128, SHA-256 ve SHA-512 karma işlevlerini destekler.


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


Bu, sizinkinden iki yıl önce Kaiido'nun cevabından nasıl farklı ?
Luc

@Luc Görünüşe göre öyle değil.
Константин Ван

3

Partiye biraz geç kaldım, ancak şu modülü kullanabilirsiniz: kripto :

const crypto = require('crypto');

const SALT = '$ome$alt';

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

Bu işlevin sonucu her zaman 64karakter dizesidir; böyle bir şey:"aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"


2

Js işlevini azaltan () ve hala eski tarayıcılarla uyumlu tarayıcılar için daha hızlı olması gereken bir işlev elde etmek için iki çözümü (kullanıcılar esmiralha ve lordvlad) birleştirdim :

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

Misal:

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

2

Çarpışmalardan kaçınmak istiyorsanız, SHA-256 gibi güvenli bir karma kullanmak isteyebilirsiniz . Birkaç JavaScript SHA-256 uygulaması vardır.

Çeşitli karma uygulamaları karşılaştırmak için testler yazdım, bkz. Https://github.com/brillout/test-javascript-hash-implementations .

Veya testleri çalıştırmak için http://brillout.github.io/test-javascript-hash-implementations/ adresine gidin .


1
Güvenli bir şifreleme karması kullanmak son derece yavaş olabilir. Çarpışmalardan kaçınmak, güvenlik değil bit genişliğinin bir ürünüdür. 128 bit kriptografik olmayan karma veya 64 bit bile çoğu amaç için fazlasıyla yeterli olmalıdır. MurmurHash3_x86_128 oldukça hızlı ve çok düşük çarpışma şansına sahip.
bryc

2

Bu, diğer cevaplardan biraz daha güvenli bir karma olmalıdır, ancak bir işlevde, önceden yüklenmiş bir kaynak olmadan

Temel olarak sha1'in küçültülmüş basitleştirilmiş bir sürümünü oluşturdum.
Dizenin baytlarını alır ve bunları 4 ila 32 bit "kelimeler" ile gruplandırırsınız.
Sonra her 8 kelimeyi 40 kelimeye uzatırız (sonuç üzerinde daha büyük etki için).
Bu, mevcut durum ve giriş ile bazı matematik yaptığımız karma fonksiyonuna (son azaltma) gider. Her zaman 4 kelime çıkarırız.
Bu, neredeyse döngüler yerine harita, azaltma ... kullanan tek komutlu / tek satırlı bir sürümdür, ancak yine de oldukça hızlıdır

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

Ayrıca kelime dizisi yerine bir dize almak için çıktıyı hex'e dönüştürürüz.
Kullanımı basit. expample için "a string".hash()dönecektir"88a09e8f9cc6f8c71c4497fbb36f84cd"


1

Onaltılı dizelere dönüştürülmüş char kodlarının basit bir birleşimi için gittim. Bu nispeten dar bir amaca hizmet eder, yani sadece bir SHORT dizesinin (örneğin başlıklar, etiketler) karma temsilinin, ilgili nedenlerden ötürü kabul edilen hashCode Java portunu kolayca uygulayamayacağı bir sunucu tarafı ile değiştirilmesi gerekir. Açıkçası burada bir güvenlik uygulaması yok.

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

Bu, Underscore ile daha dar ve tarayıcı toleranslı hale getirilebilir. Misal:

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

Benzer şekilde daha büyük dizeleri hash istiyorsanız, sadece karakter kodlarını azaltabilir ve tek tek karakterleri bir araya getirmek yerine ortaya çıkan toplamı onaltabilirsiniz:

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"

Doğal olarak bu yöntemle daha fazla çarpışma riski vardır, ancak aritmetik ile azaltabilirsiniz, ancak hash çeşitlendirmek ve uzatmak istedim.


1

@ Esmiralha'nın cevabının biraz basitleştirilmiş versiyonu.

Bu istenmeyen bazı davranışlara neden olabileceğinden, bu sürümde String'i geçersiz kılmam.

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

1

Bunu eklemek henüz kimse yapmadı, ve bu istenir ve karma ile çok şey uygulanır gibi görünüyor, ama her zaman çok kötü yapılır ...

Bu, bir dize girdisi alır ve karma değerinin eşit olmasını istediğiniz maksimum sayıyı alır ve dize girdisini temel alan benzersiz bir sayı oluşturur.

Bir dizi görüntüye benzersiz bir dizin oluşturmak için bunu kullanabilirsiniz (Bir kullanıcı için rastgele seçilen ancak adlarına göre seçilen belirli bir avatar döndürmek istiyorsanız, bu nedenle her zaman bu ada sahip birine atanacaktır. ).

Elbette bunu, bir kişinin adına göre benzersiz avatar arka plan renkleri oluşturmak gibi bir dizini bir renk dizisine döndürmek için de kullanabilirsiniz.

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

Nesne karma kütüphanesi gibi kullanıma hazır çözümler yerine bu aşırı karmaşık kripto kodunu kullanmak için herhangi bir neden göremiyorum. Satıcıya güvenmek daha verimli, zaman kazandırıyor ve bakım maliyetini düşürüyor.

Https://github.com/puleos/object-hash adresini kullanmanız yeterlidir

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

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

Bu lib'in kaynak kodu okunamıyor bile ... küçültülmüş koddan sadece 50 bin.
bryc

1
@bryc satıcı kodunun böyle görünmesi gerekiyordu :) ve kaynaklar için github.com/puleos/object-hash/blob/master/index.js
Oleg Abrazhaev

Küçültülmüş kod 35,4 KB, tam kaynak 14,2 KB? Bu hiç mantıklı değil.
bryc

2
@bryc bu çizgiyi düşündünüz mü? var crypto = require('crypto');. Ben bir derleme sırasında minimize sürümünde satıcıdan bu bağımlılık kodu ekler düşünüyorum.
Oleg Abrazhaev

Eğer gerçekten karma Nesneler gerekirse, ben yazdım herhangi-serialize sonra, sıralama tuşlarıyla HERHANGİ Nesne seri hale getirmek cyrb53 base36 hash oluşturmak.
Polv
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.