Générer un hachage à partir de la chaîne de caractères dans Javascript

je dois convertir les cordes en une forme de hachage. Est-ce possible en JavaScript?

Je n'utilise pas de langage côté serveur donc je ne peux pas le faire de cette façon.

407
demandé sur John 2011-10-01 01:52:05

18 réponses

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

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

600
répondu esmiralha 2017-10-11 18:23:21

MODIFIER

basé sur mes tests jsperf, la réponse acceptée est en fait plus rapide: http://jsperf.com/hashcodelordvlad

ORIGINAL

si quelqu'un est intéressé, voici une version améliorée ( plus rapide), qui échouera sur les navigateurs plus anciens qui n'ont pas la fonction de tableau reduce .

hashCode = function(s){
  return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);              
}
102
répondu lordvlad 2014-02-14 08:58:15

Note: même avec le meilleur hash 32 bits, vous aurez à faire avec le fait que les collisions se produiront tôt ou tard. I. e. deux chaînes d'entrée retournera la même valeur de hachage avec une probabilité d'au moins 1 : 2^32.

Dans une réponse à cette question quel algorithme de hachage est le meilleur pour l'unicité et la vitesse? , Ian Boyd a publié une bonne analyse en profondeur . Bref (selon mon interprétation), il en vient à la conclusion que le murmure est le meilleur, suivi du FNV-1a.

Java de la Chaîne.hashCode () algorithme que esmiralha a proposé semble être une variante de DJB2.

  • FNV-1a a une meilleure distribution que DJB2, mais est plus lent
  • DJB2 est plus rapide que le FNV-1a, mais tend à donner plus de collisions
  • MurmurHash3 est meilleur et plus rapide que DJB2 et FNV-1a (mais la mise en œuvre optimisée nécessite plus de lignes de code que FNV et DJB2)

quelques repères avec de grandes chaînes d'entrée ici: http://jsperf.com/32-bit-hash

Quand court chaînes d'entrée sont hachées, la performance de murmur chute, relative à DJ2B et FNV-1a: http://jsperf.com/32-bit-hash/3

donc en général I je recommande murmur3.

Voir ici pour une implémentation JavaScript: https://github.com/garycourt/murmurhash-js

si les chaînes d'entrée sont courtes et que la performance est plus importante que la qualité de la distribution, utilisez DJB2 (comme le propose la réponse acceptée par esmiralha).

si la qualité et la petite taille du code sont plus importantes que la vitesse, J'utilise cette implémentation de FNV-1a (basé sur ce 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;
}
68
répondu mar10 2017-04-12 07:31:24

basé sur réponse acceptée dans ES6. Plus petit, maintenable et fonctionne dans les navigateurs modernes.

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

// Test
console.log("hashCode(\"Hello!\"): ", hashCode('Hello!'));
35
répondu deekshith 2018-04-01 04:33:10

si cela peut aider n'importe qui, j'ai combiné les deux premières réponses dans une version plus ancienne, qui utilise la version rapide si reduce est disponible et retombe sur la solution d'esmiralha si ce n'est pas le cas.

/**
 * @see /q/generate-a-hash-from-string-in-javascript-16521/"").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;
}
"151930920 d'Utilisation", c'est comme:

var hash = new String("some string to be hashed").hashCode();
24
répondu Kyle Falconer 2014-05-15 02:17:57

c'est une variante raffinée et plus performante:

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

cela correspond à L'implémentation Java de la norme object.hashCode()

En voici un qui ne renvoie que des hashcodes positifs:

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

Et voici une correspondance pour Java qui ne renvoie que des hashcodes positifs:

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

Profitez-en!

17
répondu momo 2017-05-05 08:25:51

je suis un peu surpris que personne n'ait encore parlé de la nouvelle API SubtleCrypto .

pour obtenir un hachage à partir d'une chaîne, vous pouvez utiliser la subtle.digest méthode:

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);
  });
11
répondu Kaiido 2017-04-16 08:42:41

grâce à l'exemple de mar10, j'ai trouvé un moyen d'obtenir les mêmes résultats en C# et Javascript pour un FNV-1a. Si des caractères unicode sont présents, la partie supérieure est écartée pour des raisons de performance. Je ne sais pas pourquoi il serait utile de les maintenir lors du hashing, car je ne hashant que les chemins d'url pour le moment.

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

Version 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);
}
6
répondu djabraham 2015-05-04 18:58:37

j'avais besoin d'une fonction similaire (mais différente) pour générer un ID-ish unique basé sur le nom d'utilisateur et l'heure actuelle. 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()

produit:

2DVFXJGEKL
6IZPAKFQFL
ORGOENVMG
... etc 

edit Jun 2015: pour le nouveau code j'utilise shortid: https://www.npmjs.com/package/shortid

4
répondu jcollum 2015-06-23 17:32:08

un rapide et concis qui a été adapté de ici :

String.prototype.hashCode = function() {
  var hash = 5381, i = this.length
  while(i)
    hash = (hash * 33) ^ this.charCodeAt(--i)
  return hash >>> 0;
}
3
répondu soulmachine 2017-05-08 19:25:33

Mon rapide (très long) un liner basé sur FNV Multiply+Xor la méthode:

my_string.split('').map(v=>v.charCodeAt(0)).reduce((a,v)=>a+((a<<7)+(a<<3))^v).toString(16);
3
répondu John Smith 2017-12-03 10:02:47

si vous voulez éviter les collisions, vous pouvez utiliser un Secure hash like SHA-256 . Il existe plusieurs implémentations JavaScript SHA-256.

j'ai écrit des tests pour comparer plusieurs implémentations de hachage, voir https://github.com/brillout/test-javascript-hash-implementations .

ou passer à http://brillout.github.io/test-javascript-hash-implementations/ , pour exécuter les essais.

2
répondu brillout 2015-07-22 19:26:43

j'ai combiné les deux solutions (utilisateurs esmiralha et lordvlad) pour obtenir une fonction qui devrait être plus rapide pour les navigateurs qui prennent en charge la fonction js reduce() et encore compatible avec les vieux navigateurs:

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

exemple:

my_string = 'xyz';
my_string.hashCode();
1
répondu Frank 2015-04-29 11:07:24

j'ai opté pour une simple concaténation de codes char convertis en chaînes hexadécimales. Cela sert un but relativement étroit, à savoir avoir simplement besoin d'une représentation hash d'une chaîne courte (par exemple, titres, tags) pour être échangée avec un côté serveur qui, pour des raisons non pertinentes, ne peut pas facilement implémenter le port Java hashCode accepté. Il n'y a pas d'application de sécurité ici.

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

cela peut être rendu plus terne et plus tolérant au navigateur avec Underscore. Exemple:

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

je suppose que si vous vouliez hachez de plus grandes chaînes de la même façon, vous pourriez simplement réduire les codes de caractères et hexifier la somme résultante plutôt que de concaténer les caractères individuels ensemble:

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"

naturellement plus de risque de collision avec cette méthode, bien que vous pourriez bricoler avec l'arithmétique dans la réduire cependant vous vouliez diversifier et allonger le hachage.

1
répondu swornabsent 2015-05-06 14:16:02

version légèrement simplifiée de la réponse de @esmiralha.

Je n'outrepasse pas la chaîne dans cette version, car cela pourrait entraîner un comportement indésirable.

function hashCode(str) {
    var hash = 0;
    for (var i = 0; i < str.length; i++) {
        hash = ~~(((hash << 5) - hash) + str.charCodeAt(i));
    }
    return hash;
}
1
répondu crazy2be 2016-02-17 23:01:00

voici un simple hachage de 53 bits bien distribué. C'est assez rapide et a un faible taux de collisions.

var cyrb53 = function(str) {
    var p1 = 2654435761, p2 = 1597334677, h1 = 0xdeadbeef | 0, h2 = 0x41c6ce57 | 0;
    for (var i = 0; i < str.length; i++)
        ch = str.charCodeAt(i), h1 = Math.imul(h1 + ch, p1), h2 = Math.imul(h2 + ch, p2);
    h1 = Math.imul(h1 ^ h1 >>> 16, p2), h2 = Math.imul(h2 ^ h2 >>> 15, p1);
    return (h2 & 2097151) * 4294967296 + h1;
};

il atteint avalanche (non-strict), de sorte que les petits changements ont de grands changements dans la production, ce qui le fait paraître aléatoire:

0xe00c44e568f86 = "a"
0x893099dbedf04 = "b"
0x98f3f59367810 = "revenge"
0x45f880d099bbf = "revenue"

53-bits est la limite des entiers JS, et a beaucoup moins de chance de collision que 32-bits hashes. Mais si 53 bits ne vous suffisent pas, vous pouvez toujours utiliser les 64 bits en construisant une chaîne hexadécimale ou array:

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

le piège est, la construction de la chaîne hexagonale devient le goulot d'étranglement sur la performance, et le tableau a besoin de deux opérateurs de comparaison au lieu d'un, ce qui n'est pas aussi pratique.

1
répondu bryc 2018-10-03 23:12:09

avec cette solution, nous pouvons spécifier le jeu de caractères pour éviter certains problèmes lorsque les valeurs sont stockées ou envoyées entre les couches d'applications, par exemple: lorsque la chaîne de résultat (hachage) produit un encodage de pourcentage et que la chaîne est envoyée au contrôleur en utilisant la méthode GET à partir de la couche de vue.

function doHashCode() {
    String.prototype.hashCode = function () {
        var text = "";
        var possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

        for (var i = 0; i < 15; i++)
            text += possible.charAt(Math.floor(Math.random() * possible.length));
        return text;
    }

    var hash = new String().hashCode();
    $('#input-text-hash').val(hash); // your html input text

}
0
répondu Cassio Seffrin 2016-09-17 18:03:45

je suis un peu en retard à la fête, mais vous pouvez utiliser ce module: crypto :

const crypto = require('crypto');

const SALT = '$ome$alt';

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

le résultat de cette fonction est toujours 64 chaîne de caractères; quelque chose comme ceci: "aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"

0
répondu Frismaury 2018-08-23 16:50:26