J'ai besoin d'un identifiant unique dans .NET (je ne peux pas utiliser le GUID car il est trop long pour ce cas).
Les gens pensent-ils que l'algorithme utilisé ici est un bon candidat ou avez-vous d'autres suggestions?
J'ai besoin d'un identifiant unique dans .NET (je ne peux pas utiliser le GUID car il est trop long pour ce cas).
Les gens pensent-ils que l'algorithme utilisé ici est un bon candidat ou avez-vous d'autres suggestions?
Réponses:
Celui-ci est bon - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx
et aussi ici GUID de type YouTube
Vous pouvez utiliser Base64:
string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
Cela génère une chaîne comme E1HKfn68Pkms5zsZsvKONw ==. Comme un GUID est toujours de 128 bits, vous pouvez omettre le == dont vous savez qu'il sera toujours présent à la fin et qui vous donnera une chaîne de 22 caractères. Ce n'est pas aussi court que YouTube.
J'utilise une approche similaire à celle de Dor Cohen, mais en supprimant certains caractères spéciaux:
var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");
Cela ne produira que des caractères alphanumériques. Il n'est pas garanti que les UID aient toujours la même longueur. Voici un exemple d'exécution:
vmKo0zws8k28fR4V4Hgmw
TKbhS0G2V0KqtpHOU8e6Ug
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg
CQUg1lXIXkWG8KDFy7z6Ow
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg
var ticks = new DateTime(2016,1,1).Ticks;
var ans = DateTime.Now.Ticks - ticks;
var uniqueId = ans.ToString("x");
Conservez une date de référence (qui dans ce cas est le 1er janvier 2016) à partir de laquelle vous commencerez à générer ces identifiants. Cela rendra vos identifiants plus petits.
Numéro généré: 3af3c14996e54
milliseconds
est toujours 0 pour cet DateTime
objet
Paquet utilisable simple. Je l'utilise pour le générateur d'identifiant de demande temporelle.
https://www.nuget.org/packages/shortid
https://github.com/bolorundurowb/shortid
Les usages System.Random
string id = ShortId.Generate();
// id = KXTR_VzGVUoOY
(à partir de la page github)
Si vous souhaitez contrôler le type d'identifiant généré en spécifiant si vous voulez des nombres, des caractères spéciaux et la longueur, appelez la méthode Generate et passez trois paramètres, le premier un booléen indiquant si vous voulez des nombres, le second un booléen indiquant si vous le souhaitez caractères spéciaux, le dernier un nombre indiquant votre préférence de longueur.
string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w
Pour autant que je sache, il n'est pas garanti que le simple fait de retirer une partie d'un GUID soit unique - en fait, c'est loin d'être unique.
La chose la plus courte que je connaisse qui garantit l'unicité mondiale est présentée dans ce billet de blog de Jeff Atwood . Dans l'article lié, il discute de plusieurs façons de raccourcir un GUID et, à la fin, le réduit à 20 octets via le codage Ascii85 .
Cependant, si vous avez absolument besoin d'une solution ne dépassant pas 15 octets, je crains que vous n'ayez pas d'autre choix que d'utiliser quelque chose qui n'est pas garanti d'être unique au monde.
Les valeurs IDENTITY doivent être uniques dans une base de données, mais vous devez être conscient des limites ... par exemple, cela rend les insertions de données en masse pratiquement impossibles, ce qui vous ralentira si vous travaillez avec un très grand nombre d'enregistrements.
Vous pouvez également utiliser une valeur de date / heure. J'ai vu plusieurs bases de données où ils utilisent la date / heure pour être le PK, et même si ce n'est pas super propre, cela fonctionne. Si vous contrôlez les insertions, vous pouvez effectivement garantir que les valeurs seront uniques dans le code.
Pour mon application locale, j'utilise cette approche basée sur le temps:
/// <summary>
/// Returns all ticks, milliseconds or seconds since 1970.
///
/// 1 tick = 100 nanoseconds
///
/// Samples:
///
/// Return unit value decimal length value hex length
/// --------------------------------------------------------------------------
/// ticks 14094017407993061 17 3212786FA068F0 14
/// milliseconds 1409397614940 13 148271D0BC5 11
/// seconds 1409397492 10 5401D2AE 8
///
/// </summary>
public static string TickIdGet(bool getSecondsNotTicks, bool getMillisecondsNotTicks, bool getHexValue)
{
string id = string.Empty;
DateTime historicalDate = new DateTime(1970, 1, 1, 0, 0, 0);
if (getSecondsNotTicks || getMillisecondsNotTicks)
{
TimeSpan spanTillNow = DateTime.UtcNow.Subtract(historicalDate);
if (getSecondsNotTicks)
id = String.Format("{0:0}", spanTillNow.TotalSeconds);
else
id = String.Format("{0:0}", spanTillNow.TotalMilliseconds);
}
else
{
long ticksTillNow = DateTime.UtcNow.Ticks - historicalDate.Ticks;
id = ticksTillNow.ToString();
}
if (getHexValue)
id = long.Parse(id).ToString("X");
return id;
}
ici ma solution, n'est pas sûre pour la concurrence, pas plus de 1000 GUID par seconde et thread-safe.
public static class Extensors
{
private static object _lockGuidObject;
public static string GetGuid()
{
if (_lockGuidObject == null)
_lockGuidObject = new object();
lock (_lockGuidObject)
{
Thread.Sleep(1);
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var epochLong = Convert.ToInt64((DateTime.UtcNow - epoch).TotalMilliseconds);
return epochLong.DecimalToArbitrarySystem(36);
}
}
/// <summary>
/// Converts the given decimal number to the numeral system with the
/// specified radix (in the range [2, 36]).
/// </summary>
/// <param name="decimalNumber">The number to convert.</param>
/// <param name="radix">The radix of the destination numeral system (in the range [2, 36]).</param>
/// <returns></returns>
public static string DecimalToArbitrarySystem(this long decimalNumber, int radix)
{
const int BitsInLong = 64;
const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (radix < 2 || radix > Digits.Length)
throw new ArgumentException("The radix must be >= 2 and <= " + Digits.Length.ToString());
if (decimalNumber == 0)
return "0";
int index = BitsInLong - 1;
long currentNumber = Math.Abs(decimalNumber);
char[] charArray = new char[BitsInLong];
while (currentNumber != 0)
{
int remainder = (int)(currentNumber % radix);
charArray[index--] = Digits[remainder];
currentNumber = currentNumber / radix;
}
string result = new String(charArray, index + 1, BitsInLong - index - 1);
if (decimalNumber < 0)
{
result = "-" + result;
}
return result;
}
code non optimisé, juste un échantillon !.
UtcNow
renvoie une valeur tique unique pour chaque milliseconde: par les remarques , la résolution dépend de la minuterie du système. De plus, vous feriez mieux de vous assurer que l'horloge système ne change pas en arrière! (Étant donné que la réponse de user13971889 a placé cette question en haut de mon flux et que j'ai critiqué cette réponse, je suppose que je devrais répéter cette critique ici.)
Si votre application n'a pas quelques MILLIION personnes, en utilisant cela génère une courte chaîne unique au même MILLISECOND, vous pouvez penser à utiliser la fonction ci-dessous.
private static readonly Object obj = new Object();
private static readonly Random random = new Random();
private string CreateShortUniqueString()
{
string strDate = DateTime.Now.ToString("yyyyMMddhhmmssfff");
string randomString ;
lock (obj)
{
randomString = RandomString(3);
}
return strDate + randomString; // 16 charater
}
private string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxy";
var random = new Random();
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
remplacez aaaa par aa si vous devez simplement utiliser votre application au cours des 99 prochaines années.
Mise à jour 20160511 : Fonction aléatoire correcte
- Ajouter un objet Lock
- Déplacer une variable aléatoire hors de la fonction RandomString
Ref
lock
est de vous permettre de réutiliser la même Random
instance. Je pense que vous avez oublié de supprimer cette ligne!
Je sais que c'est assez loin de la date de publication ... :)
J'ai un générateur qui ne produit que 9 caractères hexa , par exemple: C9D6F7FF3, C9D6FB52C
public class SlimHexIdGenerator : IIdGenerator
{
private readonly DateTime _baseDate = new DateTime(2016, 1, 1);
private readonly IDictionary<long, IList<long>> _cache = new Dictionary<long, IList<long>>();
public string NewId()
{
var now = DateTime.Now.ToString("HHmmssfff");
var daysDiff = (DateTime.Today - _baseDate).Days;
var current = long.Parse(string.Format("{0}{1}", daysDiff, now));
return IdGeneratorHelper.NewId(_cache, current);
}
}
static class IdGeneratorHelper
{
public static string NewId(IDictionary<long, IList<long>> cache, long current)
{
if (cache.Any() && cache.Keys.Max() < current)
{
cache.Clear();
}
if (!cache.Any())
{
cache.Add(current, new List<long>());
}
string secondPart;
if (cache[current].Any())
{
var maxValue = cache[current].Max();
cache[current].Add(maxValue + 1);
secondPart = maxValue.ToString(CultureInfo.InvariantCulture);
}
else
{
cache[current].Add(0);
secondPart = string.Empty;
}
var nextValueFormatted = string.Format("{0}{1}", current, secondPart);
return UInt64.Parse(nextValueFormatted).ToString("X");
}
}
Basé sur la réponse de @ dorcohen et le commentaire de @ pootzko. Vous pouvez utiliser ceci. C'est sûr sur le fil.
var errorId = System.Web.HttpServerUtility.UrlTokenEncode(Guid.NewGuid().ToByteArray());
Jzhw2oVozkSNa2IkyK4ilA2
ou essayez vous-même sur dotnetfiddle.net/VIrZ8j
Sur la base de quelques autres, voici ma solution qui fournit un guid encodé différent qui est sûr pour les URL (et Docker) et ne perd aucune information:
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=", "").Replace("+", "-").Replace("/", "_");
Les exemples de sorties sont:
BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig
En C #, une long
valeur a 64 bits, qui si elle est encodée avec Base64, il y aura 12 caractères, dont 1 remplissage =
. Si nous coupons le remplissage =
, il y aura 11 caractères.
Une idée folle ici est que nous pourrions utiliser une combinaison d'époque Unix et d'un compteur pour une valeur d'époque pour former une long
valeur. L'époque Unix en C # DateTimeOffset.ToUnixEpochMilliseconds
est au long
format, mais les 2 premiers octets des 8 octets sont toujours 0, car sinon la valeur de la date et de l'heure sera supérieure à la valeur maximale de la date et de l'heure. Cela nous donne donc 2 octets pour placer un ushort
compteur.
Ainsi, au total, tant que le nombre de génération d'ID ne dépasse pas 65536 par milliseconde, nous pouvons avoir un ID unique:
// This is the counter for current epoch. Counter should reset in next millisecond
ushort currentCounter = 123;
var epoch = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
// Because epoch is 64bit long, so we should have 8 bytes
var epochBytes = BitConverter.GetBytes(epoch);
if (BitConverter.IsLittleEndian)
{
// Use big endian
epochBytes = epochBytes.Reverse().ToArray();
}
// The first two bytes are always 0, because if not, the DateTime.UtcNow is greater
// than DateTime.Max, which is not possible
var counterBytes = BitConverter.GetBytes(currentCounter);
if (BitConverter.IsLittleEndian)
{
// Use big endian
counterBytes = counterBytes.Reverse().ToArray();
}
// Copy counter bytes to the first 2 bytes of the epoch bytes
Array.Copy(counterBytes, 0, epochBytes, 0, 2);
// Encode the byte array and trim padding '='
// e.g. AAsBcTCCVlg
var shortUid = Convert.ToBase64String(epochBytes).TrimEnd('=');
public static string ToTinyUuid(this Guid guid)
{
return Convert.ToBase64String(guid.ToByteArray())[0..^2] // remove trailing == padding
.Replace('+', '-') // escape (for filepath)
.Replace('/', '_'); // escape (for filepath)
}
Usage
Guid.NewGuid().ToTinyUuid()
Ce n'est pas sorcier de se reconvertir, donc je vous laisse autant.
Si vous n'avez pas besoin de taper la chaîne, vous pouvez utiliser ce qui suit:
static class GuidConverter
{
public static string GuidToString(Guid g)
{
var bytes = g.ToByteArray();
var sb = new StringBuilder();
for (var j = 0; j < bytes.Length; j++)
{
var c = BitConverter.ToChar(bytes, j);
sb.Append(c);
j++;
}
return sb.ToString();
}
public static Guid StringToGuid(string s)
=> new Guid(s.SelectMany(BitConverter.GetBytes).ToArray());
}
Cela convertira le Guid en une chaîne de 8 caractères comme ceci:
{b77a49a5-182b-42fa-83a9-824ebd6ab58d} -> "䦥 띺 ᠫ 䋺 ꦃ 亂 檽 趵"
{c5f8f7f5-8a7c-4511-b667-8ad36b446617} -> " 엸 詼 䔑 架 펊 䑫 ᝦ"
Voici ma petite méthode pour générer un identifiant unique aléatoire et court. Utilise un rng cryptographique pour la génération sécurisée de nombres aléatoires. Ajoutez les caractères dont vous avez besoin à la chars
chaîne.
private string GenerateRandomId(int length)
{
char[] stringChars = new char[length];
byte[] randomBytes = new byte[length];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomBytes);
}
string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[randomBytes[i] % chars.Length];
}
return new string(stringChars);
}
pour ne pas perdre de caractères (+ / -) et si vous voulez utiliser votre guid dans une url, il faut le transformer en base32
pour 10 000 000 pas de clé en double
public static List<string> guids = new List<string>();
static void Main(string[] args)
{
for (int i = 0; i < 10000000; i++)
{
var guid = Guid.NewGuid();
string encoded = BytesToBase32(guid.ToByteArray());
guids.Add(encoded);
Console.Write(".");
}
var result = guids.GroupBy(x => x)
.Where(group => group.Count() > 1)
.Select(group => group.Key);
foreach (var res in result)
Console.WriteLine($"Duplicate {res}");
Console.WriteLine($"*********** end **************");
Console.ReadLine();
}
public static string BytesToBase32(byte[] bytes)
{
const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
string output = "";
for (int bitIndex = 0; bitIndex < bytes.Length * 8; bitIndex += 5)
{
int dualbyte = bytes[bitIndex / 8] << 8;
if (bitIndex / 8 + 1 < bytes.Length)
dualbyte |= bytes[bitIndex / 8 + 1];
dualbyte = 0x1f & (dualbyte >> (16 - bitIndex % 8 - 5));
output += alphabet[dualbyte];
}
return output;
}
Vous pouvez essayer avec la bibliothèque suivante:
private static readonly object _getUniqueIdLock = new object();
public static string GetUniqueId()
{
lock(_getUniqueIdLock)
{
System.Threading.Thread.Sleep(1);
return DateTime.UtcNow.Ticks.ToString("X");
}
}
UtcNow
renvoie une valeur de graduation unique pour chaque milliseconde: selon les remarques , la résolution dépend de la minuterie du système. De plus, vous feriez mieux de vous assurer que l'horloge système ne change pas en arrière! (La réponse de ur3an0 a également ces problèmes.)
vous pouvez utiliser
code = await UserManager.GenerateChangePhoneNumberTokenAsync(input.UserId, input.MobileNumber);
ses 6
beaux caractères seulement, 599527
,143354
et lorsque l'utilisateur le virifie simplement
var result = await UserManager.VerifyChangePhoneNumberTokenAsync(input.UserId, input.Token, input.MobileNumber);
j'espère que cela vous aidera
Guid.NewGuid().ToString().Split('-').First()
J'utilise Guid.NewGuid().ToString().Split('-')[0]
, il obtient le premier élément du tableau séparé par le '-'. Il suffit de représenter une clé unique.