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.
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/
MODIFIER
sur la base de mes tests jsperf, la réponse acceptée est en réalité 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 dépourvus de 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);
}
Note: Même avec le meilleur hachage 32 bits, vous devrez faire face au fait Que les collisions vont se produisent tôt ou tard. C'est à dire. Deux chaînes d'entrée différentes renverront la même valeur de hachage avec une probabilité d'au moins 1: 2 ^ 32.
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 en profondeur .. En bref (selon mon interprétation), il arrive à la conclusion que Murmur est le meilleur choix, suivi de FNV-1a.
L'algorithme String.hashCode () de Java proposé par esmiralha semble être une variante de DJB2.
Quelques repères avec de grandes chaînes d’entrée ici: http://jsperf.com/32-bit-hash
Lorsque courtes chaînes d'entrée sont hachées, les performances du murmure baissent 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 de 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 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 rapidité, 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;
}
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!'));
Si cela peut aider quelqu'un, j'ai combiné les deux réponses principales dans une version plus ancienne tolérante pour le navigateur, qui utilise la version rapide si reduce
est disponible et utilise la solution esmiralha si ce n'est pas le cas.
/**
* @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 = new String("some string to be hashed").hashCode();
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;
};
Ceci correspond à l'implémentation Java de la object.hashCode()
standard
Voici également celui qui renvoie uniquement les codes de hachage positifs:
String.prototype.hashcode = function() {
return (this.hashCode() + 2147483647) + 1;
};
Et voici une correspondance pour Java qui ne retourne que des hashcodes positifs:
public static long hashcode(Object obj) {
return ((long) obj.hashCode()) + Integer.MAX_VALUE + 1l;
}
Prendre plaisir!
Je suis un peu surpris que personne n'ait encore parlé de la nouvelle - API SubtleCrypto .
Pour obtenir un hachage d'une chaîne, vous pouvez utiliser la méthode subtle.digest
:
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);
});
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 performance. Je ne sais pas pourquoi il serait utile de maintenir ceux-ci lors du hachage, car je ne hachais que les chemins des 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);
}
Voici un hachage 53 bits simple et bien distribué. Il est assez rapide et a un taux de collision nettement inférieur à celui d'un hachage 32 bits.
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);
};
Il utilise des techniques similaires à xxHash/MurmurHash3, mais pas aussi complètes. Il provoque une avalanche (non stricte), de sorte que de petits changements dans l'entrée ont de grands changements dans la sortie, ce qui le fait apparaître de manière aléatoire:
0xc2ba782c97901 = cyrb53("a")
0xeda5bc254d2bf = cyrb53("b")
0xe64cc3b748385 = cyrb53("revenge")
0xd85148d13f93a = cyrb53("revenue")
Vous pouvez également fournir une graine pour d'autres flux de la même entrée:
0xee5e6598ccd5c = cyrb53("revenue", 1)
0x72e2831253862 = cyrb53("revenue", 2)
0x0de31708e6ab7 = cyrb53("revenue", 3)
Techniquement, c'est un hachage 64 bits, mais JavaScript est limité aux entiers 53 bits. Les 64 bits complets peuvent toujours être utilisés en modifiant la ligne de retour pour une chaîne ou un tableau hexadécimal:
return (h2>>>0).toString(16).padStart(8,0)+(h1>>>0).toString(16).padStart(8,0);
// or
return [h2>>>0, h1>>>0];
Le problème est que la construction de la chaîne hexadécimale devient le goulot d'étranglement des performances et que le tableau a besoin de deux opérateurs de comparaison au lieu d'un, ce qui n'est pas aussi pratique. Alors gardez cela à l'esprit lorsque vous l'utilisez pour des applications hautes performances.
Et juste pour le plaisir, voici un hachage minimal de 32 bits en 89 caractères qui bat encore 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}
Mon rapide (très long) one liner basé sur la méthode Multiply+Xor
de FNV:
my_string.split('').map(v=>v.charCodeAt(0)).reduce((a,v)=>a+((a<<7)+(a<<3))^v).toString(16);
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;
}
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. Alors:
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
Si vous voulez éviter les collisions, vous pouvez utiliser un secure hash like SHA-256 . Il existe plusieurs implémentations de 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 allez à http://brillout.github.io/test-javascript-hash-implementations/ , pour exécuter les tests.
J'ai combiné les deux solutions (esmiralha et lordvlad) pour obtenir une fonction 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();
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;
}
Je n’utilise pas de langage côté serveur, je ne peux donc pas le faire de cette façon.
Etes-vous sûr de ne pas pouvoir le faire 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.")));
})();
Je suis allé pour une concaténation simple de codes de caractères convertis en chaînes hexadécimales. Cela sert un objectif relativement étroit, à savoir qu’il suffit d’avoir une représentation en hachage d’une chaîne SHORT (par exemple des titres, des balises) à échanger avec un côté serveur qui, pour des raisons non pertinentes, ne peut pas implémenter facilement 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 le 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 de la réduction, mais vous vouliez diversifier et allonger le hachage.
En ajoutant cela parce que personne ne l’a encore fait, et cela semble être demandé et mis en œuvre souvent avec des hachages, mais cela se fait toujours très mal….
Cela prend une entrée de chaîne et un nombre maximal 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 créer un index unique dans un tableau d'images (si vous souhaitez renvoyer un avatar spécifique pour un utilisateur, choisi au hasard, mais également en fonction de son nom, il sera donc toujours attribué à une personne portant ce nom. ).
Vous pouvez également utiliser cela, bien sûr, pour retourner un index dans un tableau de couleurs, par exemple pour générer des couleurs d'arrière-plan uniques pour un avatar basées sur le nom d'une personne.
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);
}
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"