Générer un hachage à partir d'une chaîne en Javascript


589

J'ai besoin de convertir des chaînes en une forme de hachage. Est-ce possible en JavaScript?

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


7
MD5 n'est pas sécurisé, alors ne cherchez pas celui-là.
henrikstroem

166
@henrikstroem Dépend de ce que vous hachez; il n'y a rien de mal à utiliser md5 pour créer un hachage à des fins non liées à la sécurité.
Brad Koch

7
@BradKoch Dépend de ce que vous faites; il n'y a rien de mal à utiliser md5 à des fins de sécurité. Il existe certainement de meilleures méthodes pour hacher les mots de passe, mais md5 est très bien pour faire des choses comme signer une URL.
Paul Ferrett

81
Je trouve drôle que bien que MD5 soit critiqué dans les commentaires ici, presque toutes les réponses recommandent des algorithmes de hachage bien pires et obtiennent beaucoup de votes positifs.
domen

38
L'utilisation de MD5 pour vérifier qu'un téléchargement est resté intact ne va pas comme par magie envoyer vos mots de passe à tous vos collègues.
James M. Lay

Réponses:


790
Object.defineProperty(String.prototype, 'hashCode', {
  value: function() {
    var hash = 0, i, chr;
    for (i = 0; i < this.length; i++) {
      chr   = this.charCodeAt(i);
      hash  = ((hash << 5) - hash) + chr;
      hash |= 0; // Convert to 32bit integer
    }
    return hash;
  }
});

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


22
C'est le même que celui utilisé en Java. C'est hash << 5 - hashla même chose hash * 31 + charmais beaucoup plus rapide. C'est bien parce que c'est tellement rapide, et 31 est un petit nombre premier. Gagner y gagner.
corsiKa

41
J'ai fait quelques tests sur jsperf ( jsperf.com/hashing-strings ) et la fonction au niveau du bit est en fait plus lente que la fonction basée sur les nombres.
skerit

17
@PeterAronZentai Pourquoi est-il "inutilisable"? La sortie produite par le code basé sur les nombres (hash * 31) + charest identique à la sortie produite par le code basé sur le décalage ((hash<<5)-hash)+char, même pour les chaînes très longues (je l'ai testé avec des chaînes contenant plus d'un million de caractères), donc ce n'est pas "inutilisable" en termes de précision. La complexité est O (n) pour les versions basées sur les nombres et sur les équipes, donc elle n'est pas "inutilisable" en termes de complexité.
TachyonVortex

13
Quelqu'un peut-il commenter le caractère unique (ou non) de la sortie? Plus précisément, si j'utilise uniquement ce hachage pour les chaînes de longueur inférieure à n, quelle est la plus grande npour laquelle je ne peux pas avoir de collision?
Don McCurdy

34
Y a-t-il une raison pour laquelle cela doit (ou devrait) être sur le prototype String? Serait-il moins efficace / efficient d'avoir simplement par exemple; var hashCode = function hashCode (str) {etc...}? Et puis utiliser comme hashCode("mystring")?
rattray

146

ÉDITER

sur la base de 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 reducefonction tableau.

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

version de fonction flèche à une ligne:

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

3
existe-t-il un moyen d'obtenir du hachage qui est uniquement un nombre positif?
Prosto Trader

46
bizarre. je viens de le tester et il s'est avéré être plus lent que la réponse acceptée. jsperf.com/hashcodelordvlad
lordvlad

113
Bon gars @lordvlad, testant en fait sa propre réponse, puis signalant quand elle était plus lente.
mikemaccana

9
Je viens de réaliser: Il est parfaitement logique que la réponse acceptée soit plus rapide, car ma version doit d'abord transformer la chaîne en un tableau, allouer de la nouvelle mémoire et copier chaque caractère ...
lordvlad

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

108

Remarque: même avec le meilleur hachage 32 bits, des collisions se produiront tôt ou tard.

La probabilité de collision de hachage peut être calculée comme 1 - e ^ (-k (k-1) / 2N, approximée par k ^ 2 / 2N ( voir ici ). Cela peut être plus élevé que ne le suggère l'intuition: en
supposant un hachage 32 bits et k = 10 000 éléments, une collision se produira avec une probabilité de 1,2%. Pour 77 163 échantillons, la probabilité devient 50%! ( calculatrice ).
Je suggère une solution de contournement en bas.

En réponse à cette question Quel algorithme de hachage est le meilleur pour l'unicité et la vitesse? , Ian Boyd a publié une bonne analyse approfondie . En bref (comme je l'interprète), il arrive à la conclusion que Murmur est le meilleur, suivi de FNV-1a.
L'algorithme String.hashCode () de Java proposé par esmiralha semble être une variante de DJB2.

  • FNV-1a a une meilleure distribution que DJB2, mais est plus lent
  • DJB2 est plus rapide que FNV-1a, mais a tendance à produire plus de collisions
  • MurmurHash3 est meilleur et plus rapide que DJB2 et FNV-1a (mais l'implémentation optimisée nécessite plus de lignes de code que FNV et DJB2)

Quelques benchmarks avec de grandes chaînes d'entrée ici: http://jsperf.com/32-bit-hash
Lorsque des chaînes d'entrée courtes sont hachées, les performances de murmure chutent, par rapport à DJ2B et FNV-1a: http://jsperf.com/32- bit-hash / 3

Donc, en général, je recommanderais 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 les performances sont plus importantes que la qualité de distribution, utilisez DJB2 (comme proposé par 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ée 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;
}

Améliorez la probabilité de collision

Comme expliqué ici , nous pouvons étendre la taille des bits de hachage en utilisant cette astuce:

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

Utilisez-le avec soin et n'en attendez pas trop.


Pourquoi tu fais ça ("0000000" + (hval >>> 0).toString(16)).substr(-8);? N'est-ce pas la même chose que (hval >>> 0).toString(16)?
Manuel Meurer

3
ceci ajoute des «0» de premier plan pour que le hachage résultant soit toujours de 8 caractères. Plus facile à lire et à reconnaître dans les sorties, mais c'est mon opinion personnelle
mar10

Ah ok, je comprends. Pour les petits hval, il (hval >>> 0).toString(16)peut contenir moins de 8 caractères, vous devez donc le remplir avec des zéros. J'étais juste confus, car (hval >>> 0).toString(16)il en résultait toujours pour moi une chaîne de 8 caractères exactement.
Manuel Meurer

3
J'adore cette réponse car elle produit un hachage distribué beaucoup mieux: d'autres fonctions proposées ici feront des valeurs de hachage conséquentes. Par exemple `hash (" example1 ") - hash (" example2 ") == 1", alors que celui-ci est beaucoup plus imprévisible.
GavinoGrifoni

1
En réponse à "FNV-1a a une meilleure distribution que DJB2, mais est plus lent" - je pense qu'il faut dire que FNV1a peut être extrêmement rapide lorsqu'il est implémenté en utilisant la Math.imulfonction ES6 . Cela en fait à lui seul des références de référence, et finalement un meilleur choix que DJB2 à long terme.
bryc

64

Basé sur la 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!'));

EDIT (2019-11-04) :

version de fonction flèche à une ligne:

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

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


1
Merci pour le partage que j'ai ajouté str += ""avant le hachage pour éviter les exceptions str.split is not a functionlevées lorsque des non-chaînes ont été passées en tant que paramètres
BeetleJuice

4
Mais beaucoup, beaucoup plus lentement que n'importe lequel d'entre eux: https://jsperf.com/hashing-strings
AndyO

Je viens également de remarquer que la solution "rétro" la plus rapide est en fait plus petite également si vous supprimez les sauts de ligne pour qu'elle ne fasse que 3 lignes.
AndyO

2
Y a-t-il un moyen pour que cela ne produise que des résultats positifs mais toujours uniques?
Dids

3
@deekshith La réponse acceptée utilise hash |= 0pour convertir en un entier 32 bits. Cette implémentation ne fonctionne pas. Est-ce un bug?
Sukima

48

Près de la moitié des réponses sont des implémentations de Java String.hashCode, qui ne sont ni de haute qualité ni super rapides. Ce n'est rien de trop spécial, il multiplie juste par 31 pour chaque personnage. Il peut être implémenté simplement et efficacement sur une seule ligne, et est beaucoup plus rapide avec Math.imul:

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

Avec cela à l'écart, voici quelque chose de mieux - cyrb53 , un hachage 53 bits simple mais de haute qualité. Il est assez rapide, offre une très bonne distribution de hachage et présente des taux de collision nettement inférieurs à ceux de tout hachage 32 bits.

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

Semblable aux algorithmes MurmurHash / xxHash bien connus, il utilise une combinaison de multiplication et de Xorshift pour générer le hachage, mais pas aussi complètement. En conséquence, c'est plus rapide qu'en JavaScript et beaucoup plus simple à mettre en œuvre.

Il réalise une avalanche (non stricte), ce qui signifie essentiellement que de petits changements dans l'entrée ont de grands changements dans la sortie, ce qui fait que le hachage résultant semble aléatoire:

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

Vous pouvez également fournir une valeur de départ pour des flux alternatifs de la même entrée:

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

Techniquement, il s'agit d'un hachage 64 bits (deux hachages 32 bits non corrélés en parallèle), mais JavaScript est limité à des entiers 53 bits. Si nécessaire, la sortie 64 bits complète peut toujours être utilisée en modifiant la ligne de retour pour une chaîne hexadécimale ou un tableau.

Sachez que la construction de chaînes hexadécimales peut considérablement ralentir le traitement par lots dans des situations critiques pour les performances.

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

Et juste pour le plaisir, voici un hachage 32 bits minimal en 89 caractères avec une qualité supérieure à celle de FNV ou 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, c'est tellement mieux que l'habituel * 31 pour les entrées courtes (ou similaires). :)
lapo

2
Où est chinitialisé?
hellowill89

3
@ hellowill89 woops, j'ai oublié de le déclarer et saignait dans la portée mondiale. réparé maintenant, merci: ')
bryc

Échec pour IE 11: l'objet ne prend pas en charge la propriété ou la méthode 'imul'.
BachT

2
@BachT Vous pouvez utiliser un polyfill ou une cale ES6 complète . Mais IE11 est tragiquement gelé en 2009 sans mises à jour.
bryc

28

Si cela aide quelqu'un, j'ai combiné les deux premières réponses dans une version plus ancienne, reducecompatible avec les navigateurs, qui utilise la version rapide si elle est disponible et revient à la solution d'esmiralha si elle ne l'est pas.

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

L'utilisation est comme:

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

comment optimiser ce code pour qu'il s'exécute plus rapidement dans chaque navigateur. 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

Il s'agit d'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()

Voici également celui qui ne renvoie que des codes de hachage positifs:

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

Et voici un correspondant pour Java qui ne renvoie que des codes de hachage positifs:

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

Prendre plaisir!


2
bonne réponse, mais quel est le but de << 0?
koolaang

8
@koolaang c'est l'opérateur de la merde gauche, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mmm

29
@momomo Vouliez-vous dire shift gauche ?
wdh

2
@momomo Je pense qu'il demandait pourquoi c'était un décalage à gauche de zéro bit.
jpfx1342

3
@Maykonn (2 ^
32-1

24

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.digestmé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);
  });


4
Je suis d'accord. La conversion en hexadécimal pourrait se faire un peu différemment ...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
Une fonction de hachage cryptographique pour les chaînes est un peu exagérée .. crypton'est pas exactement performante.
bryc

Qualité aléatoire fiable sans avoir à compter sur des personnes exécutant des tests, intégré (aucune implémentation personnalisée requise), pouvant être semé, et je n'avais besoin que de quelques centaines de chiffres pour générer une carte de jeu, cela semblait parfait. Mais il s'avère qu'il n'y a absolument aucun moyen de le faire de manière synchrone. Devoir fournir un truc de rappel asynchrone à chaque fois que vous appelez votre moteur aléatoire prédéfini rend le code super illisible et semble ridicule. Je ne sais pas qui est venu avec cette interface crypto.subtle merde, donc j'ai finalement dû choisir xmur3 + sfc32 à partir de cette réponse: stackoverflow.com/a/47593316/1201863
Luc

7

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 supprimée pour des raisons de performances. Je ne sais pas pourquoi il serait utile de les conserver lors du hachage, car je ne hache que les chemins d'URL pour l'instant.

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

@mathiasrw Il est possible que les caractères Unicode dépassent 8 bits en mémoire, donc je suppose que le 0xFF masque simplement tout ce qui se trouve en dehors de cette plage. En savoir plus sur charCodeAt () ici: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
djabraham

Si ES6 est disponible (tous les moteurs modernes le prennent en charge), Math.imulpeut être utilisé pour l'étape de multiplication, ce qui améliore considérablement les performances . Le seul problème est que cela ne fonctionnera pas dans IE11 sans cale .
bryc

6

Un rapide et concis qui a été adapté à partir d' ici :

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

5

J'avais besoin d'une fonction similaire (mais différente) pour générer un identifiant unique basé sur le nom d'utilisateur et l'heure actuelle. Donc:

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 

modifier juin 2015: pour le nouveau code, j'utilise shortid: https://www.npmjs.com/package/shortid


2
@ t0r0X bien maintenant j'utilise un module appelé shortid: npmjs.com/package/shortid
jcollum

1
Comment utilisez-vous le nom d'utilisateur avec shortid? Il semble simplement générer des identifiants, mais je ne vois pas comment vous utilisez pour générer un hachage à partir d'une chaîne
cyberwombat

1
Cette réponse a 3 downvotes. Pour ma vie, je ne peux pas imaginer pourquoi. Personne n'a rien dit ...: - /
jcollum

1
@jcollum c'est pourquoi je ne réponds presque jamais aux questions périmées. même après avoir corrigé la réponse, personne ne vient pour l'équilibrer.
bryc

5

Ma doublure rapide (très longue) basée sur la Multiply+Xorméthode FNV :

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

5

SubtleCrypto.digest

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

Êtes-vous sûr de ne pas pouvoir procéder de cette façon ?

Avez-vous oublié que vous utilisez Javascript, la langue en constante évolution?

Essayez SubtleCrypto. Il prend en charge les fonctions de hachage SHA-1, SHA-128, SHA-256 et 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.")));
})();


En quoi est-ce différent de la réponse de Kaiido deux ans avant la vôtre ?
Luc

@Luc Ce n'est pas le cas, apparemment.
Константин Ван

3

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 une 64chaîne de caractères; quelque chose comme ça:"aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"


2

J'ai combiné les deux solutions (utilisateurs esmiralha et lordvlad) pour obtenir une fonction qui devrait être plus rapide pour les navigateurs prenant en charge la fonction js reduction () et toujours compatible avec les anciens 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();

2

Si vous souhaitez éviter les collisions, vous pouvez utiliser un hachage sécurisé comme 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 accédez à http://brillout.github.io/test-javascript-hash-implementations/ , pour exécuter les tests.


1
L'utilisation d'un hachage cryptographique sécurisé peut être extrêmement lente. Éviter les collisions est un produit de la largeur du bit et non de la sécurité. Le hachage non cryptographique 128 bits ou même 64 bits devrait être plus que suffisant pour la plupart des applications. MurmurHash3_x86_128 est assez rapide et a un très faible risque de collisions.
bryc

2

Cela devrait être un hachage un peu plus sécurisé que certaines autres réponses, mais dans une fonction, sans source préchargée

J'ai essentiellement créé une version simplifiée simplifiée de sha1.
Vous prenez les octets de la chaîne et les regroupez par "mots" de 4 à 32 bits.
Ensuite, nous étendons tous les 8 mots à 40 mots (pour un impact plus important sur le résultat).
Cela va à la fonction de hachage (la dernière réduction) où nous faisons quelques calculs avec l'état actuel et l'entrée. Nous obtenons toujours 4 mots.
C'est presque une version à une commande / une ligne utilisant la carte, réduire ... au lieu de boucles, mais c'est toujours assez rapide

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

nous convertissons également la sortie en hexadécimal pour obtenir une chaîne au lieu d'un tableau de mots.
L'utilisation est simple. par exemple "a string".hash()reviendra"88a09e8f9cc6f8c71c4497fbb36f84cd"


1

J'ai opté pour une simple concaténation des codes de caractères convertis en chaînes hexadécimales. Cela sert un objectif relativement étroit, à savoir avoir juste besoin d'une représentation de hachage d'une chaîne SHORT (par exemple, titres, balises) à échanger avec un côté serveur qui, pour des raisons non pertinentes, ne peut pas facilement implémenter le port Java hashCode accepté. Évidemment aucune 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 concis et tolérant au navigateur avec Underscore. Exemple:

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

Je suppose que si vous vouliez hacher des chaînes plus grandes de la même manière, 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 risques de collision avec cette méthode, bien que vous puissiez jouer avec l'arithmétique dans la réduction, mais vous vouliez diversifier et allonger le hachage.


1

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

Je ne remplace pas String 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

Ajouter cela parce que personne ne l'a encore fait, et cela semble être demandé et implémenté beaucoup avec des hachages, mais c'est toujours très mal fait ...

Cela prend une entrée de chaîne et un nombre maximum que vous souhaitez que le hachage soit égal et produit un nombre unique basé sur l'entrée de chaîne.

Vous pouvez l'utiliser pour produire un index unique dans un tableau d'images (si vous souhaitez renvoyer un avatar spécifique pour un utilisateur, choisi au hasard, mais également choisi en fonction de son nom, il sera donc toujours attribué à quelqu'un avec ce nom ).

Vous pouvez également l'utiliser, bien sûr, pour renvoyer un index dans un tableau de couleurs, comme pour générer des couleurs d'arrière-plan d'avatar uniques en fonction du nom de quelqu'un.

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

Je ne vois aucune raison d'utiliser ce code cryptographique trop compliqué au lieu de solutions prêtes à l'emploi, comme une bibliothèque de hachage d'objets, etc., compter sur le fournisseur est plus productif, fait gagner du temps et réduit les coûts de maintenance.

Utilisez simplement https://github.com/puleos/object-hash

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

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

Le code source de cette bibliothèque n'est même pas lisible .. juste 50k de code minifié.
bryc

1
@bryc voilà à quoi le code fournisseur devrait ressembler :) et pour les sources, vous pouvez consulter github.com/puleos/object-hash/blob/master/index.js
Oleg Abrazhaev

Le code réduit est de 35,4 Ko tandis que la source complète est de 14,2 Ko? Ça n'a aucun sens.
bryc

2
@bryc avez-vous envisagé cette ligne? var crypto = require('crypto');. Je pense qu'il ajoute ce code de dépendance du fournisseur dans la version minifiée lors d'une build.
Oleg Abrazhaev du

Si vous avez vraiment besoin de hacher des objets, j'ai écrit any-serialize pour sérialiser N'IMPORTE QUEL objet avec des clés de tri, puis cyrb53 pour générer du hachage base36.
Polv
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.