Generieren Sie einen Hash aus einer Zeichenfolge in Javascript


585

Ich muss Strings in eine Art Hash konvertieren. Ist das in JavaScript möglich?

Ich verwende keine serverseitige Sprache, daher kann ich das nicht so machen.


7
MD5 ist nicht sicher, suchen Sie also nicht nach diesem.
Henrikstroem

166
@henrikstroem Kommt darauf an, was du hasst; Es ist nichts Falsches daran, mit md5 einen Hash für nicht sicherheitsrelevante Zwecke zu erstellen.
Brad Koch

7
@BradKoch Kommt darauf an, was du tust; Es ist nichts Falsches daran, md5 aus Sicherheitsgründen zu verwenden. Es gibt sicherlich bessere Methoden zum Hashing von Passwörtern, aber md5 eignet sich gut zum Signieren einer URL.
Paul Ferrett

81
Ich finde es lustig, dass MD5 zwar in Kommentaren hier kritisiert wird, aber fast alle Antworten viel schlechtere Hash-Algorithmen empfehlen und viele positive Stimmen erhalten.
Domen

38
Wenn Sie MD5 verwenden, um zu überprüfen, ob ein Download intakt ist, werden Ihre Passwörter nicht auf magische Weise per E-Mail an alle Ihre Mitarbeiter gesendet.
James M. Lay

Antworten:


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

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


22
Dies ist das gleiche, das in Java verwendet wird. Das hash << 5 - hashist das gleiche wie hash * 31 + charaber viel schneller. Es ist schön, weil es so schnell ist und 31 eine kleine Primzahl ist. Win Win dort.
CorsiKa

41
Ich habe ein paar Tests mit jsperf ( jsperf.com/hashing-strings ) durchgeführt und die bitweise Funktion ist tatsächlich langsamer als die zahlenbasierte Funktion.
Skerit

17
@PeterAronZentai Warum ist es "unbrauchbar"? Die Ausgabe des zahlenbasierten Codes (hash * 31) + charist identisch mit der Ausgabe des Shift-basierten Codes ((hash<<5)-hash)+char, selbst für sehr lange Zeichenfolgen (ich habe sie mit Zeichenfolgen mit mehr als einer Million Zeichen getestet), sodass sie nicht "unbrauchbar" ist der Genauigkeit. Die Komplexität ist O (n) sowohl für die zahlenbasierte als auch für die verschiebungsbasierte Version, daher ist sie hinsichtlich der Komplexität nicht "unbrauchbar".
TachyonVortex

13
Kann jemand die Eindeutigkeit (oder nicht) der Ausgabe kommentieren? Wenn ich diesen Hash nur für Zeichenfolgen mit einer Länge von weniger als verwende n, was ist der größte, nfür den ich möglicherweise keine Kollision haben kann?
Don McCurdy

34
Gibt es einen Grund, warum dies für den String-Prototyp erforderlich ist (oder sein sollte)? Wäre es weniger effektiv / effizient, nur z. var hashCode = function hashCode (str) {etc...}? Und dann verwenden als hashCode("mystring")?
Rattray

146

BEARBEITEN

Basierend auf meinen jsperf-Tests ist die akzeptierte Antwort tatsächlich schneller: http://jsperf.com/hashcodelordvlad

ORIGINAL

Wenn jemand interessiert ist, ist hier eine verbesserte (schnellere) Version, die bei älteren Browsern, denen die reduceArray-Funktion fehlt, fehlschlägt .

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

Version mit einzeiliger Pfeilfunktion:

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

3
Gibt es eine Möglichkeit, Hash zu erhalten, der nur eine positive Zahl ist?
Prosto Trader

46
seltsam. Ich habe es gerade getestet und es stellte sich heraus, dass es langsamer war als die akzeptierte Antwort. jsperf.com/hashcodelordvlad
lordvlad

113
Guter Kerl @lordvlad, der tatsächlich seine eigene Antwort testet und dann meldet, wenn es langsamer ist.
Mikemaccana

9
Ich habe gerade festgestellt: Es macht durchaus Sinn, dass die akzeptierte Antwort schneller ist, da meine Version die Zeichenfolge zuerst in ein Array
umwandeln muss

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

108

Hinweis: Selbst mit dem besten 32-Bit - Hash - Kollisionen werden früher oder später auftreten.

Die Hash - Kollision probablility kann berechnet werden 1 - e ^ (-k (k-1) / 2N, wie aproximated k ^ 2 / 2N ( siehe hier ). Dies kann höher sein, als die Intuition vermuten lässt:
Unter der Annahme eines 32-Bit-Hashs und k = 10.000 Elementen tritt eine Kollision mit einer Wahrscheinlichkeit von 1,2% auf. Für 77.163 Proben beträgt die Wahrscheinlichkeit 50%! ( Taschenrechner ).
Ich schlage unten eine Problemumgehung vor.

In einer Antwort auf diese Frage Welcher Hashing-Algorithmus eignet sich am besten für Eindeutigkeit und Geschwindigkeit? Ian Boyd hat eine gründliche Analyse veröffentlicht . Kurz gesagt (wie ich es interpretiere) kommt er zu dem Schluss, dass Murmeln am besten ist, gefolgt von FNV-1a.
Der von esmiralha vorgeschlagene String.hashCode () -Algorithmus von Java scheint eine Variante von DJB2 zu sein.

  • FNV-1a hat eine bessere Verteilung als DJB2, ist aber langsamer
  • DJB2 ist schneller als FNV-1a, führt jedoch tendenziell zu mehr Kollisionen
  • MurmurHash3 ist besser und schneller als DJB2 und FNV-1a (aber die optimierte Implementierung erfordert mehr Codezeilen als FNV und DJB2)

Einige Benchmarks mit großen Eingabezeichenfolgen hier: http://jsperf.com/32-bit-hash
Wenn kurze Eingabezeichenfolgen gehasht werden, sinkt die Leistung des Murmelns im Vergleich zu DJ2B und FNV-1a: http://jsperf.com/32- Bit-Hash / 3

Im Allgemeinen würde ich murmur3 empfehlen.
Eine JavaScript-Implementierung finden Sie hier: https://github.com/garycourt/murmurhash-js

Wenn die Eingabezeichenfolgen kurz sind und die Leistung wichtiger ist als die Verteilungsqualität, verwenden Sie DJB2 (wie in der akzeptierten Antwort von esmiralha vorgeschlagen).

Wenn Qualität und kleine Codegröße wichtiger sind als Geschwindigkeit, verwende ich diese Implementierung von FNV-1a (basierend auf diesem Code ).

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

Kollisionswahrscheinlichkeit verbessern

Wie hier erklärt , können wir die Hash-Bitgröße mit diesem Trick erweitern:

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

Gehen Sie vorsichtig damit um und erwarten Sie nicht zu viel.


Warum machst du ("0000000" + (hval >>> 0).toString(16)).substr(-8);? Ist das nicht dasselbe wie (hval >>> 0).toString(16)?
Manuel Meurer

3
Dies addiert führende Nullen, so dass der resultierende Hash immer 8 Zeichen lang ist. Leichter zu lesen und in Ausgaben zu erkennen, aber das ist meine persönliche Meinung
10. März

Ah ok, ich verstehe. Für kleine hval, (hval >>> 0).toString(16)vielleicht weniger als 8 Zeichen lang sein, so dass Sie Pad mit Nullen. Ich war nur verwirrt, weil ich (hval >>> 0).toString(16)immer genau 8 Zeichen hatte.
Manuel Meurer

3
Ich liebe diese Antwort, weil sie einen sooo viel besser verteilten Hash erzeugt: Andere hier vorgeschlagene Funktionen ergeben konsequente Hash-Werte. ZB `Hash (" Beispiel1 ") - Hash (" Beispiel2 ") == 1", während dieser viel unvorhersehbarer ist.
GavinoGrifoni

1
Als Antwort auf "FNV-1a hat eine bessere Verteilung als DJB2, ist aber langsamer" - ich denke, es sollte gesagt werden, dass FNV1a extrem schnell sein kann, wenn es mit der ES6- Math.imulFunktion implementiert wird . Das allein macht es zu Top-Benchmarks und letztendlich zu einer besseren Wahl als DJB2 auf lange Sicht.
Bryc

64

Basierend auf der akzeptierten Antwort in ES6. Kleiner, wartbar und funktioniert in modernen Browsern.

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

Version mit einzeiliger Pfeilfunktion:

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

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


1
Vielen Dank für das Teilen, das ich str += ""vor dem Hashing hinzugefügt habe , um Ausnahmen zu vermeiden, die str.split is not a functionausgelöst wurden, wenn Nicht-Strings als Parameter übergeben wurden
BeetleJuice

4
Aber viel, viel langsamer als alle anderen: https://jsperf.com/hashing-strings
AndyO

Mir ist auch gerade aufgefallen, dass die schnellste "Retro" -Lösung auch kleiner ist, wenn Sie die Zeilenvorschübe so entfernen, dass sie nur 3 Zeilen lang sind.
AndyO

2
Gibt es eine Möglichkeit, dies nur zu positiven, aber dennoch einzigartigen Ergebnissen zu führen?
Dids

3
@deekshith Die akzeptierte Antwort wird verwendet hash |= 0, um in ein 32-Bit-Int zu konvertieren. Diese Implementierung funktioniert nicht. Ist das ein Fehler?
Sukima

48

Fast die Hälfte der Antworten sind Implementierungen von Java String.hashCode, die weder von hoher Qualität noch superschnell sind. Es ist nichts Besonderes, es multipliziert sich nur mit 31 für jeden Charakter. Es kann einfach und effizient in einer Zeile implementiert werden und ist viel schneller mit Math.imul:

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

Hier ist etwas Besseres - cyrb53 , ein einfacher, aber qualitativ hochwertiger 53-Bit-Hash. Es ist ziemlich schnell, bietet eine sehr gute Hash-Verteilung und deutlich niedrigere Kollisionsraten als jeder 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);
};

Ähnlich wie bei den bekannten MurmurHash / xxHash-Algorithmen wird eine Kombination aus Multiplikation und Xorshift verwendet , um den Hash zu generieren, jedoch nicht so gründlich. Dadurch ist es schneller als JavaScript und wesentlich einfacher zu implementieren.

Es wird eine Lawine (nicht streng) erreicht, was im Grunde bedeutet, dass kleine Änderungen in der Eingabe große Änderungen in der Ausgabe haben, wodurch der resultierende Hash zufällig erscheint:

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

Sie können auch einen Startwert für alternative Streams derselben Eingabe angeben:

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

Technisch gesehen handelt es sich um einen 64-Bit-Hash (zwei nicht korrelierte 32-Bit-Hashes parallel), aber JavaScript ist auf 53-Bit-Ganzzahlen beschränkt. Bei Bedarf kann die vollständige 64-Bit-Ausgabe weiterhin verwendet werden, indem die Rückgabezeile für eine Hex-Zeichenfolge oder ein Array geändert wird.

Beachten Sie, dass das Erstellen von Hex-Zeichenfolgen die Stapelverarbeitung in leistungskritischen Situationen drastisch verlangsamen kann.

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

Und nur zum Spaß, hier ist ein minimaler 32-Bit-Hash in 89 Zeichen mit höherer Qualität als sogar FNV oder 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
Wow, das ist so viel besser als das übliche * 31 für kurze (oder ähnliche) Eingaben. :)
Lapo

2
Wo wird chinitialisiert?
hellowill89

3
@ hellowill89 woops, ich habe vergessen, es zu deklarieren und blutete in den globalen Bereich. jetzt behoben, danke: ')
bryc

Fehlgeschlagen für IE 11: Objekt unterstützt keine Eigenschaft oder Methode 'imul'.
BachT

2
@BachT Sie können eine Polyfill oder eine vollständige ES6-Unterlegscheibe verwenden . Aber IE11 ist 2009 auf tragische Weise ohne Updates eingefroren.
Bryc

28

Wenn es jemandem hilft, habe ich die beiden reducewichtigsten Antworten zu einer Version kombiniert, die ältere Browser toleriert. Diese Version verwendet die schnelle Version, sofern verfügbar, und greift auf die Lösung von esmiralha zurück, wenn dies nicht der Fall ist.

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

Verwendung ist wie:

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

So optimieren Sie diesen Code, damit er in jedem Browser schneller ausgeführt wird. 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

Dies ist eine raffinierte und leistungsstärkere Variante:

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

Dies entspricht der Implementierung des Standards durch Java object.hashCode()

Hier ist auch eine, die nur positive Hashcodes zurückgibt:

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

Und hier ist eine passende für Java, die nur positive Hashcodes zurückgibt:

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

Genießen!


2
gute Antwort, aber was ist der Zweck von << 0?
Koolaang

8
@koolaang es ist der linke Scheißoperator, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mmm

29
@momomo Haben Sie links bedeuten Verschiebung ?
wdh

2
@momomo Ich glaube, er hat gefragt, warum es eine Linksverschiebung von null Bits war.
jpfx1342

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

24

Ich bin ein bisschen überrascht, dass noch niemand über die neue SubtleCrypto-API gesprochen hat.

Um einen Hash aus einer Zeichenfolge abzurufen, können Sie die folgende subtle.digestMethode verwenden:

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
Genau. Die Umstellung auf Hex könnte etwas anders erfolgen ...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
Eine kryptografische Hash-Funktion für Zeichenfolgen ist etwas übertrieben. Sie cryptoist nicht gerade performant.
Bryc

Zuverlässige Qualität zufällig, ohne sich auf Leute verlassen zu müssen, die Tests ausführen, integriert (keine benutzerdefinierte Implementierung erforderlich), setzbar, und ich brauchte nur ein paar hundert Zahlen, um eine Spielkarte zu erstellen. Dies schien perfekt zu sein. Es stellt sich jedoch heraus, dass es absolut keine Möglichkeit gibt, dies synchron zu tun. Wenn Sie jedes Mal, wenn Sie Ihre gesetzte Zufalls-Engine aufrufen, einen asynchronen Rückruf bereitstellen müssen, ist der Code super unlesbar und sieht lächerlich aus. Ich verstehe nicht, wer auf diese beschissene crypto.subtle-Oberfläche gekommen ist, also musste ich am Ende mit xmur3 + sfc32 aus dieser Antwort gehen: stackoverflow.com/a/47593316/1201863
Luc

7

Dank des Beispiels von mar10 habe ich einen Weg gefunden, die gleichen Ergebnisse in C # AND Javascript für einen FNV-1a zu erzielen. Wenn Unicode-Zeichen vorhanden sind, wird der obere Teil aus Gründen der Leistung verworfen. Ich weiß nicht, warum es hilfreich wäre, diese beim Hashing beizubehalten, da ich momentan nur URL-Pfade hashe.

C # -Version

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-Version

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 Es ist möglich, dass Unicode-Zeichen 8 Bit im Speicher überschreiten, daher gehe ich davon aus, dass 0xFF einfach alles außerhalb dieses Bereichs maskiert. Weitere Informationen zu charCodeAt () finden Sie hier: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
djabraham

Wenn ES6 verfügbar ist (alle modernen Motoren unterstützen es), Math.imulkann es für den Multiplikationsschritt verwendet werden, wodurch die Leistung erheblich verbessert wird . Das einzige Problem ist, dass es in IE11 ohne Shim nicht funktioniert .
Bryc

6

Eine schnelle und prägnante, die von hier angepasst wurde :

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

5

Ich brauchte eine ähnliche Funktion (aber anders), um eine eindeutige ID basierend auf dem Benutzernamen und der aktuellen Uhrzeit zu generieren. Damit:

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

Produziert:

2DVFXJGEKL
6IZPAKFQFL
ORGOENVMG
... etc 

edit Jun 2015: Für neuen Code verwende ich shortid: https://www.npmjs.com/package/shortid


2
@ t0r0X Nun, ich benutze ein Modul namens shortid: npmjs.com/package/shortid
jcollum

1
Wie benutzt man den Benutzernamen mit shortid? Es scheint nur IDs zu generieren, aber ich sehe nicht, wie Sie einen Hash aus einem String generieren
Cyberwombat

1
Diese Antwort hat 3 Downvotes. Für mein Leben kann ich mir nicht vorstellen warum. Niemand hat etwas gesagt ...: - /
jcollum

1
@jcollum deshalb beantworte ich fast nie abgestandene Fragen .. hit and runs bleiben unbemerkt. Selbst nachdem Sie die Antwort festgelegt haben, kommt niemand, um sie auszugleichen.
Bryc

5

Mein schneller (sehr langer) Einzeiler basierend auf der Multiply+XorMethode von FNV :

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

5

SubtleCrypto.digest

Ich verwende keine serverseitige Sprache, daher kann ich das nicht so machen.

Sind Sie sicher , können Sie es nicht tun , dass die Art und Weise ?

Haben Sie vergessen, dass Sie Javascript verwenden, die Sprache, die sich ständig weiterentwickelt?

Versuchen Sie es SubtleCrypto. Es unterstützt die Hash-Funktionen SHA-1, SHA-128, SHA-256 und 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.")));
})();


Wie unterscheidet sich das von der Antwort von Kaiido zwei Jahre vor Ihrer ?
Luc

@ Luc Es ist anscheinend nicht.
4онстантин Ван

3

Ich bin etwas spät zur Party, aber Sie können dieses Modul verwenden: crypto :

const crypto = require('crypto');

const SALT = '$ome$alt';

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

Das Ergebnis dieser Funktion ist immer eine 64Zeichenfolge. etwas wie das:"aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"


2

Ich habe die beiden Lösungen (Benutzer esmiralha und lordvlad) kombiniert, um eine Funktion zu erhalten, die für Browser, die die js-Funktion reduct () unterstützen, schneller sein sollte und dennoch mit alten Browsern kompatibel ist:

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

Beispiel:

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

2

Wenn Sie Kollisionen vermeiden möchten, können Sie einen sicheren Hash wie SHA-256 verwenden . Es gibt mehrere JavaScript SHA-256-Implementierungen.

Ich habe Tests geschrieben, um mehrere Hash-Implementierungen zu vergleichen, siehe https://github.com/brillout/test-javascript-hash-implementations .

Oder gehen Sie zu http://brillout.github.io/test-javascript-hash-implementations/ , um die Tests auszuführen.


1
Die Verwendung eines sicheren kryptografischen Hash kann sehr langsam sein. Das Vermeiden von Kollisionen ist ein Produkt der Bitbreite, nicht der Sicherheit. Nicht-kryptografischer 128-Bit-Hash oder sogar 64-Bit sollten für die meisten Zwecke mehr als ausreichend sein. MurmurHash3_x86_128 ist ziemlich schnell und hat eine sehr geringe Wahrscheinlichkeit von Kollisionen.
Bryc

2

Dies sollte ein bisschen sicherer sein als einige andere Antworten, jedoch in einer Funktion ohne vorinstallierte Quelle

Ich habe im Grunde eine minimierte vereinfachte Version von sha1 erstellt.
Sie nehmen die Bytes der Zeichenfolge und gruppieren sie nach 4 bis 32 Bit "Wörtern".
Dann erweitern wir alle 8 Wörter auf 40 Wörter (für eine größere Auswirkung auf das Ergebnis).
Dies geht zur Hashing-Funktion (der letzten Reduzierung), wo wir mit dem aktuellen Status und der Eingabe einige Berechnungen durchführen. Wir bekommen immer 4 Wörter raus.
Dies ist fast eine Ein-Befehls- / Ein-Zeilen-Version mit Map, Reduce ... anstelle von Schleifen, aber es ist immer noch ziemlich schnell

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

Wir konvertieren auch die Ausgabe in hex, um eine Zeichenfolge anstelle eines Wortarrays zu erhalten.
Die Verwendung ist einfach. zum Beispiel "a string".hash()wird zurückkehren"88a09e8f9cc6f8c71c4497fbb36f84cd"


1

Ich habe mich für eine einfache Verkettung von Zeichencodes entschieden, die in Hex-Zeichenfolgen konvertiert wurden. Dies dient einem relativ engen Zweck, nämlich lediglich einer Hash-Darstellung einer SHORT-Zeichenfolge (z. B. Titel, Tags), die mit einer Serverseite ausgetauscht werden muss, die aus nicht relevanten Gründen den akzeptierten HashCode-Java-Port nicht einfach implementieren kann. Offensichtlich keine Sicherheitsanwendung hier.

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

Dies kann mit Underscore knapper und browserverträglicher gemacht werden. Beispiel:

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

Ich nehme an, wenn Sie größere Zeichenfolgen auf ähnliche Weise hashen möchten, können Sie einfach die Zeichencodes reduzieren und die resultierende Summe hexifizieren, anstatt die einzelnen Zeichen miteinander zu verketten:

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"

Natürlich mehr Kollisionsrisiko mit dieser Methode, obwohl Sie mit der Arithmetik in der Reduzierung herumspielen könnten, aber Sie wollten den Hash diversifizieren und verlängern.


1

Leicht vereinfachte Version von @ esmiralhas Antwort.

Ich überschreibe String in dieser Version nicht, da dies zu unerwünschtem Verhalten führen kann.

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

1

Fügen Sie dies hinzu, weil es noch niemand getan hat, und dies scheint viel gefragt und mit Hashes implementiert zu sein, aber es wird immer sehr schlecht gemacht ...

Dies erfordert eine Zeichenfolgeneingabe und eine maximale Anzahl, der der Hash entsprechen soll, und erzeugt eine eindeutige Zahl basierend auf der Zeichenfolgeneingabe.

Sie können dies verwenden, um einen eindeutigen Index für ein Array von Bildern zu erstellen (Wenn Sie einen bestimmten Avatar für einen Benutzer zurückgeben möchten, der zufällig ausgewählt, aber auch anhand seines Namens ausgewählt wurde, wird er immer einer Person mit diesem Namen zugewiesen ).

Sie können dies natürlich auch verwenden, um einen Index in ein Array von Farben zurückzugeben, z. B. um eindeutige Avatar-Hintergrundfarben basierend auf dem Namen einer Person zu generieren.

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

Ich sehe keinen Grund, diesen überkomplizierten Kryptocode anstelle von gebrauchsfertigen Lösungen wie Objekt-Hash-Bibliotheken usw. zu verwenden. Die Abhängigkeit vom Anbieter ist produktiver, spart Zeit und reduziert die Wartungskosten.

Verwenden Sie einfach https://github.com/puleos/object-hash

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

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

Der Quellcode dieser Bibliothek ist nicht einmal lesbar. Nur 50.000 minimierter Code.
Bryc

1
@bryc so soll Vendor Code aussehen :) und für Quellen können Sie github.com/puleos/object-hash/blob/master/index.js
Oleg Abrazhaev

Der minimierte Code beträgt 35,4 KB, während die vollständige Quelle 14,2 KB beträgt. Das macht keinen Sinn.
Bryc

2
@bryc hast du diese Zeile in Betracht gezogen? var crypto = require('crypto');. Ich denke, es fügt diesen Abhängigkeitscode des Herstellers in der minimierten Version während eines Builds hinzu.
Oleg Abrazhaev

Wenn Sie Objekte wirklich hashen müssen, habe ich any-serialize geschrieben , um JEDES Objekt mit Sortierschlüsseln zu serialisieren, und dann cyrb53 , um base36-Hash zu generieren.
Polv
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.