it-swarm.com.de

Generieren Sie einen Hash aus String in Javascript

Ich muss Strings in irgendeine Form von Hash konvertieren. Ist das in JavaScript möglich?

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

444
FreeSnow
String.prototype.hashCode = function() {
  var hash = 0, i, chr;
  if (this.length === 0) return hash;
  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/

658
esmiralha

EDIT

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

ORIGINAL

wenn Sie interessiert sind, finden Sie hier eine verbesserte (schnellere) Version, die bei älteren Browsern fehlschlägt, denen die reduce-Array-Funktion fehlt.

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

Hinweis: Selbst mit dem besten 32-Bit-Hash müssen Sie sich damit auseinandersetzen Dass Kollisionen will früher oder später auftreten. Das heißt Zwei verschiedene Eingabezeichenfolgen geben denselben Hashwert mit einer Wahrscheinlichkeit von mindestens 1: 2 ^ 32 zurück.

In einer Antwort auf diese Frage Welcher Hash-Algorithmus ist am besten für Eindeutigkeit und Geschwindigkeit? , Ian Boyd hat eine gute eingehende Analyse veröffentlicht . Kurz gesagt (als ich es interpretiere) kommt er zu dem Schluss, dass Murmur am besten ist, gefolgt von FNV-1a.
Der von esmiralha vorgeschlagene Algorithmus String.hashCode () 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, neigt jedoch zu mehr Kollisionen
  • MurmurHash3 ist besser und schneller als DJB2 und FNV-1a (die optimierte Implementierung erfordert jedoch mehr Codezeilen als FNV und DJB2).

Einige Benchmarks mit großen Eingabezeichenfolgen hier: http://jsperf.com/32-bit-hash
Wenn short Eingabestrings gehasht werden, sinkt die Leistung des Murmels relativ zu DJ2B und FNV-1a: http://jsperf.com/32-bit-hash/3

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

Wenn die Eingabestrings 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 als Geschwindigkeit sind, 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;
}
80
mar10

Basierend auf akzeptierte 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!'));

42
deekshith

Wenn es jemandem hilft, kombinierte ich die ersten beiden Antworten in einer Version, die für ältere Browser tolerant ist. Diese Version verwendet die schnelle Version, wenn reduce verfügbar ist, und greift auf die Lösung von esmiralha zurück, falls 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 = new String("some string to be hashed").hashCode();
24
Kyle Falconer

Dies ist eine verfeinerte und leistungsfähigere 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 Java-Implementierung des Standards object.hashCode()

Hier ist auch einer, der nur positive Hashcodes zurückgibt: 

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

Und hier ist ein passender für Java, der nur positive Hashcodes zurückgibt: 

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

Genießen!

18
momomo

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

Um einen Hash aus einem String zu erhalten, können Sie die Methode subtle.digest 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);
  });

14
Kaiido

Dank des Beispiels von mar10 habe ich einen Weg gefunden, dieselben Ergebnisse in C # UND Javascript für eine FNV-1a zu erhalten. 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 aufrechtzuerhalten, da derzeit nur Hash-URL-Pfade verwendet werden.

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

Hier ist ein einfacher, gut verteilter 53-Bit-Hash. Es ist ziemlich schnell und hat im Vergleich zu einem 32-Bit-Hash deutlich niedrigere Kollisionsraten.

var cyrb53 = function(str, seed = 0) {
    var h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
    for (var 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);
};

Es verwendet Techniken, die xxHash/MurmurHash3 ähnlich sind, aber nicht so gründlich. Es erreicht eine Lawine (nicht-streng), so dass kleine Änderungen in der Eingabe große Änderungen in der Ausgabe bewirken, sodass sie zufällig erscheinen:

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 handelt es sich um einen 64-Bit-Hash, aber JavaScript ist auf 53-Bit-Ganzzahlen beschränkt. Die vollen 64 Bits können weiterhin verwendet werden, indem die Rückleitung für einen Hex-String oder ein Array geändert wird:

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

Der Haken ist, das Erstellen des Hex-Strings wird zum Engpass bei der Leistung, und das Array benötigt zwei Vergleichsoperatoren anstelle von einem. Beachten Sie dies, wenn Sie es für Hochleistungsanwendungen verwenden.


Und zum Spaß, hier ist ein minimaler 32-Bit-Hash in 89 Zeichen, der immer noch besser ist als FNV/DJB2/SMDB:

TSH=s=>{for(var i=0,h=6;i<s.length;)h=Math.imul(h^s.charCodeAt(i++),9**9);return h^h>>>9}
6
bryc

Mein schneller (sehr langer) Liner basierend auf der Multiply+Xor-Methode von FNV:

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

Eine schnelle und prägnante Version, 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;
}
4
soulmachine

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

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 

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

4
jcollum

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

Ich habe Tests geschrieben, um verschiedene 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.

2
brillout

Ich habe die beiden Lösungen (Benutzer esmiralha und lordvlad) kombiniert, um eine Funktion zu erhalten, die für Browser, die die js-Funktion verkleinern () unterstützen, schneller sein sollte und 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
Frank

Leicht vereinfachte Version der Antwort von @esmiralha.

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
crazy2be

SubtleCrypto.digest

Ich verwende keine serverseitige Sprache, daher kann ich dies nicht tun.

Sind Sie sicher, dass Sie das nicht so machen können ?

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

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

Ich entschied mich für eine einfache Verkettung von Zeichencodes, die in Hex-Zeichenketten umgewandelt wurden. Dies dient einem relativ engen Zweck, nämlich dass nur eine Hash-Darstellung einer SHORT-Zeichenfolge (z. B. Titel, Tags) mit einer Serverseite ausgetauscht werden muss, die aus nicht relevanten Gründen den akzeptierten Hash-Code-Java-Port nicht ohne weiteres 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 browsertolerant werden. Beispiel:

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

Ich vermute, wenn Sie größere Strings auf ähnliche Weise hashten, könnten Sie einfach die Zeichencodes reduzieren und die resultierende Summe verhexen, 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 Risiko für eine Kollision mit dieser Methode, obwohl Sie mit der Arithmetik in der Herangehensweise spielen können, obwohl Sie den Hash diversifizieren und verlängern wollten.

1
swornabsent

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

Dies erfordert eine Zeichenfolgeeingabe und eine maximale Anzahl, bei der der Hashwert gleich sein soll, und erzeugt eine eindeutige Zahl basierend auf der Zeichenfolgeeingabe.

Sie können dies verwenden, um einen eindeutigen Index für eine Reihe 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 wird, 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
Nick Steele

Ich bin ein bisschen 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 64 Zeichenfolge; etwas wie das: "aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a" 

0
Frismaury