118 votes

Comment créer des guides déterministes

Dans notre application, nous créons des fichiers Xml avec un attribut qui a une valeur Guid. Cette valeur doit être cohérente entre les mises à jour du fichier. Ainsi, même si tout le reste du fichier change, la valeur Guid de l'attribut doit rester la même.

Une solution évidente consistait à créer un dictionnaire statique contenant les noms de fichiers et les Guids à utiliser pour ceux-ci. Ensuite, chaque fois que nous générons le fichier, nous consultons le dictionnaire pour le nom de fichier et utilisons le guide correspondant. Mais ce n'est pas faisable car nous pourrions passer à des centaines de fichiers et nous ne voulons pas maintenir une grande liste de guides.

Une autre approche consistait donc à rendre le Guid identique en fonction du chemin du fichier. Puisque nos chemins de fichiers et la structure du répertoire de l'application sont uniques, le guide devrait être unique pour ce chemin. Ainsi, chaque fois que nous exécutons une mise à jour, le fichier reçoit le même guide en fonction de son chemin. J'ai trouvé une façon sympa de générer de tels ' Guides déterministes (Merci Elton Stoneman). En gros, cela donne ceci :

private Guid GetDeterministicGuid(string input) 

{ 

//use MD5 hash to get a 16-byte hash of the string: 

MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); 

byte[] inputBytes = Encoding.Default.GetBytes(input); 

byte[] hashBytes = provider.ComputeHash(inputBytes); 

//generate a guid from the hash: 

Guid hashGuid = new Guid(hashBytes); 

return hashGuid; 

} 

Ainsi, étant donné une chaîne de caractères, le Guid sera toujours le même.

Y a-t-il d'autres approches ou d'autres méthodes recommandées pour ce faire ? Quels sont les avantages ou les inconvénients de cette méthode ?

163voto

Bradley Grainger Points 12126

Comme mentionné par @bacar, RFC 4122 Le §4.3 définit un moyen de créer un UUID basé sur un nom. L'avantage de cette méthode (par rapport à l'utilisation d'un simple hachage MD5) est qu'elle garantit l'absence de collision avec des UUID non basés sur le nom, et que la possibilité de collision avec d'autres UUID basés sur le nom est très (très) faible.

Il n'y a pas de support natif dans le .NET Framework pour créer ceux-ci, mais j'ai affiché code sur GitHub qui met en œuvre l'algorithme. Il peut être utilisé comme suit :

Guid guid = GuidUtility.Create(GuidUtility.UrlNamespace, filePath);

Pour réduire encore plus le risque de collisions avec d'autres GUID, vous pouvez créer un GUID privé à utiliser comme ID d'espace de nom (au lieu d'utiliser l'ID d'espace de nom de l'URL défini dans la RFC).

0 votes

Notez que bien que cela soit utile, l'implémentation n'est pas tout à fait conforme à la RFC4122, donc si vous essayez d'être compatible avec une autre implémentation, vous aurez des difficultés (essayez l'exemple dans le code C dans l'annexe RFC).

5 votes

@Porges : RFC4122 est incorrect et a un errata qui corrige le code C ( rfc-editor.org/errata_search.php?rfc=4122&eid=1352 ). Si cette implémentation n'est pas entièrement conforme à la RFC4122 et à ses errata, veuillez fournir des détails supplémentaires ; j'aimerais qu'elle suive la norme.

1 votes

@BradleyGrainger : Je n'avais pas remarqué, merci/désolé ! Je devrais toujours penser à vérifier l'errata lorsque je lis un RFC... :)

30voto

Ben Gripka Points 4885

Ceci convertira n'importe quelle chaîne de caractères en un Guid sans avoir à importer un assemblage extérieur.

public static Guid ToGuid(string src)
{
    byte[] stringbytes = Encoding.UTF8.GetBytes(src);
    byte[] hashedBytes = new System.Security.Cryptography
        .SHA1CryptoServiceProvider()
        .ComputeHash(stringbytes);
    Array.Resize(ref hashedBytes, 16);
    return new Guid(hashedBytes);
}

Il existe de bien meilleures façons de générer un Guid unique, mais il s'agit d'un moyen de mettre à niveau de manière cohérente une clé de données de type chaîne vers une clé de données de type Guid.

0 votes

Cet extrait s'est avéré utile lors de l'utilisation d'un identifiant unique dans une base de données pour une distribution fédérée.

8 votes

Attention ! Ce code ne génère pas de Guids / UUIDs valides (comme bacar le mentionne également ci-dessous). Ni le champ version ni le champ type ne sont définis correctement.

3 votes

Ne serait-il pas tout aussi efficace d'utiliser le MD5CryptoServiceProvider au lieu du SHA1, puisque le MD5 a déjà une longueur de 16 octets ?

22voto

bacar Points 2017

Comme le mentionne Rob, votre méthode ne génère pas un UUID, elle génère un hash qui ressemble à un UUID.

El RFC 4122 sur les UUIDs permet spécifiquement les UUIDs déterministes (basés sur le nom) - Les versions 3 et 5 utilisent md5 et SHA1 (respectivement). La plupart des gens sont probablement familiers avec la version 4, qui est aléatoire. Wikipedia donne un bon aperçu des versions. (Notez que l'utilisation du mot "version" ici semble décrire un "type" d'UUID - la version 5 ne remplace pas la version 4).

Il semble qu'il existe quelques bibliothèques permettant de générer des UUID version 3/5, dont la module python uuid , boost.uuid (C++) et OSSP UUID . (Je n'ai pas cherché de .net).

1 votes

C'est exactement ce que recherche le posteur original. UUID dispose déjà d'un algorithme permettant de partir d'une chaîne de caractères et de la convertir en un GUID. La version 3 de UUID hache la chaîne avec MD5, tandis que la version 5 la hache avec SHA1. Le point important dans la création d'un "guid" est de le rendre "unique" par rapport aux autres GUID. L'algorithme définit deux bits qui doivent être activés, ainsi qu'un nibble qui prend la valeur 3 ou 5, selon qu'il s'agit de la version 3 ou 5.

2 votes

En ce qui concerne l'utilisation du mot "version", le RFC 4122 §4.1.3 indique : " La version est plus exactement un sous-type ; là encore, nous conservons le terme par souci de compatibilité. "

12 votes

J'ai posté du code C# pour créer des GUIDs v3 et v5 sur GitHub : github.com/LogosBible/Logos.Utility/blob/master/src/

3voto

Rob Fonseca-Ensor Points 11697

Vous devez faire une distinction entre les instances de la classe Guid et des identifiants qui sont uniques au monde. Un "guide déterministe" est en fait un hash (comme le prouve votre appel à provider.ComputeHash ). Les hachages ont beaucoup plus de chances d'entrer en collision (deux chaînes différentes produisant le même hachage) que les guides créés par l'intermédiaire de Guid.NewGuid .

Le problème avec votre approche est donc que vous devrez accepter la possibilité que deux chemins différents produisent le même GUID. Si vous avez besoin d'un identifiant unique pour une chaîne de chemin donnée, la chose la plus simple à faire est de utilisez simplement la chaîne . Si vous avez besoin que la chaîne soit cachée à vos utilisateurs, le crypter - vous pouvez utiliser ROT13 ou quelque chose de plus puissant...

Tenter de faire entrer dans le type de données GUID quelque chose qui n'est pas un pur GUID pourrait entraîner des problèmes de maintenance à l'avenir...

2 votes

Vous affirmez que "les hachages ont beaucoup plus de chances d'entrer en collision ... que les guides créés via Guid.NewGuid". Pouvez-vous élaborer sur ce point ? D'un point de vue mathématique, le nombre de bits que l'on peut définir est le même, et MD5 et SHA1 sont des hachages cryptographiques, spécifiquement conçus pour réduire la probabilité de collisions de hachage (accidentelles et intentionnelles).

0 votes

Je dirais que la principale différence est que les hachages cryptographiques mappent d'un espace infini à un autre espace fixe en utilisant une fonction. Imaginez un hachage qui fait correspondre des chaînes de longueur variable à 128 bits alors que Guid génère des 128 bits pseudo-aléatoires. La génération pseudo-aléatoire ne repose pas sur une entrée initiale mais plutôt sur la génération de la sortie de manière uniforme dans l'espace de sortie en utilisant un caractère aléatoire provenant du matériel ou d'autres moyens.

1voto

ryber Points 3117

MD5 est faible, je pense que vous pouvez faire la même chose avec SHA-1 et obtenir de meilleurs résultats.

BTW, juste une opinion personnelle, habiller un hash md5 comme un GUID n'en fait pas un bon GUID. Les GUIDs, par leur nature même, ne sont pas déterministes. Pourquoi ne pas appeler un chat un chat et dire simplement qu'il s'agit d'un hachage rendu par une chaîne de caractères de l'entrée. Vous pourriez le faire en utilisant cette ligne, plutôt que la nouvelle ligne guid :

string stringHash = BitConverter.ToString(hashBytes)

0 votes

Merci pour votre contribution, mais cela me donne toujours une chaîne de caractères, et je cherche un GUID...

0 votes

Ok, appelez votre hachage un "GUID", problème résolu. Ou le vrai problème est-il que vous besoin de a Guid objet ?

0 votes

J'aimerais que ce soit aussi simple :) mais oui, j'ai besoin d'un objet 'GUID'.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X