Ho bisogno di un identificatore univoco in .NET (non posso usare GUID perché è troppo lungo per questo caso).
Le persone pensano che l'algoritmo utilizzato qui sia un buon candidato o hai altri suggerimenti?
Ho bisogno di un identificatore univoco in .NET (non posso usare GUID perché è troppo lungo per questo caso).
Le persone pensano che l'algoritmo utilizzato qui sia un buon candidato o hai altri suggerimenti?
Risposte:
Questo uno buono - http://www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx
e anche qui GUID simile a YouTube
Puoi usare Base64:
string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
Questo genera una stringa come E1HKfn68Pkms5zsZsvKONw ==. Poiché un GUID è sempre a 128 bit, puoi omettere il == che sai sarà sempre presente alla fine e che ti darà una stringa di 22 caratteri. Tuttavia, non è così breve come YouTube.
Uso un approccio simile a quello di Dor Cohen ma rimuovendo alcuni caratteri speciali:
var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");
Questo produrrà solo caratteri alfanumerici. Non è garantito che gli UID abbiano sempre la stessa lunghezza. Ecco un esempio di esecuzione:
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");
Mantieni una data di riferimento (che in questo caso è il 1 ° gennaio 2016) da quando inizierai a generare questi ID. Questo renderà i tuoi ID più piccoli.
Numero generato: 3af3c14996e54
milliseconds
è sempre 0 per DateTime
quell'oggetto
Pacchetto utilizzabile semplice. Lo uso per il generatore di id di richiesta temporale.
https://www.nuget.org/packages/shortid
https://github.com/bolorundurowb/shortid
Utilizza System.Random
string id = ShortId.Generate();
// id = KXTR_VzGVUoOY
(dalla pagina GitHub)
Se vuoi controllare il tipo di id generato specificando se vuoi numeri, caratteri speciali e la lunghezza, chiama il metodo Generate e passa tre parametri, il primo un booleano che indica se vuoi numeri, il secondo un booleano che indica se vuoi caratteri speciali, l'ultimo un numero che indica la tua preferenza di lunghezza.
string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w
Per quanto ne so, la semplice rimozione di una parte di un GUID non è garantita per essere unica , anzi, è tutt'altro che unica.
La cosa più breve che io sappia che garantisce l'unicità globale è descritta in questo post del blog di Jeff Atwood . Nel post collegato, discute diversi modi per abbreviare un GUID e alla fine lo riduce a 20 byte tramite la codifica Ascii85 .
Tuttavia, se hai assolutamente bisogno di una soluzione che non sia più lunga di 15 byte, temo che tu non abbia altra scelta che usare qualcosa che non è garantito essere unico a livello globale.
I valori IDENTITY dovrebbero essere univoci in un database, ma dovresti essere consapevole dei limiti ... ad esempio, rende praticamente impossibili gli inserimenti di dati in blocco, il che ti rallenterà se stai lavorando con un numero molto elevato di record.
Potresti anche essere in grado di utilizzare un valore di data / ora. Ho visto diversi database in cui usano la data / ora come PK e, sebbene non sia super pulito, funziona. Se controlli gli inserti, puoi effettivamente garantire che i valori saranno univoci nel codice.
Per la mia app locale sto usando questo approccio basato sul tempo:
/// <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;
}
qui la mia soluzione, non è sicura per la concorrenza, non più di 1000 GUID al secondo e 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;
}
codice non ottimizzato, solo campione !.
UtcNow
restituisca un valore di tick univoco per ogni millisecondo: secondo le osservazioni , la risoluzione dipende dal timer di sistema. Inoltre, faresti meglio ad assicurarti che l'orologio di sistema non cambi all'indietro! (Dal momento che la risposta di user13971889 ha spostato questa domanda in cima al mio feed e ho criticato quella risposta, immagino che dovrei ripetere quella critica qui.)
Se la tua app non ha poche MILIONI di persone, utilizzando quella stringa univoca che genera lo STESSO MILLISECONDO, puoi pensare di utilizzare la funzione di seguito.
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());
}
cambia yyyy in yy se devi solo usare la tua app nei prossimi 99 anni.
Aggiornamento 20160511 : funzione casuale corretta
- Aggiungi oggetto Lock
- Sposta la variabile casuale fuori dalla funzione RandomString
Ref
lock
è che ti consente di riutilizzare la stessa Random
istanza. Penso che ti sei dimenticato di cancellare quella riga!
So che è abbastanza lontano dalla data di pubblicazione ... :)
Ho un generatore che produce solo 9 caratteri Hexa , ad esempio: 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");
}
}
Basato sulla risposta di @ dorcohen e sul commento di @ pootzko. Puoi usarlo. È sicuro sul filo.
var errorId = System.Web.HttpServerUtility.UrlTokenEncode(Guid.NewGuid().ToByteArray());
Jzhw2oVozkSNa2IkyK4ilA2
o prova te stesso su dotnetfiddle.net/VIrZ8j
Sulla base di alcuni altri, ecco la mia soluzione che fornisce una guida codificata diversa che è sicura per l'URL (e Docker) e non perde alcuna informazione:
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=", "").Replace("+", "-").Replace("/", "_");
Gli output di esempio sono:
BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig
In C # un long
valore ha 64 bit, che se codificati con Base64, ci saranno 12 caratteri, incluso 1 riempimento =
. Se tagliamo l'imbottitura =
, ci saranno 11 caratteri.
Un'idea folle qui è che potremmo usare una combinazione di Unix Epoch e un contatore per un valore di epoch per formare un long
valore. L'epoca Unix in C # DateTimeOffset.ToUnixEpochMilliseconds
è in long
formato, ma i primi 2 byte degli 8 byte sono sempre 0, perché altrimenti il valore di data e ora sarà maggiore del valore massimo di data e ora. Quindi questo ci dà 2 byte per inserire un ushort
contatore.
Quindi, in totale, fintanto che il numero di generazione di ID non supera 65536 per millisecondo, possiamo avere un ID univoco:
// 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)
}
Utilizzo
Guid.NewGuid().ToTinyUuid()
Non è scienza missilistica riconvertirsi, quindi ti lascio così tanto.
Se non è necessario digitare la stringa, è possibile utilizzare quanto segue:
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());
}
Questo convertirà il Guid in una stringa di 8 caratteri come questa:
{b77a49a5-182b-42fa-83a9-824ebd6ab58d} -> "䦥 띺 ᠫ 䋺 ꦃ 亂 檽 趵"
{c5f8f7f5-8a7c-4511-b667-8ad36b446617} -> " 엸 詼 䔑 架 펊 䑫 ᝦ"
Ecco il mio piccolo metodo per generare un ID univoco casuale e breve. Utilizza un rng crittografico per la generazione sicura di numeri casuali. Aggiungi tutti i caratteri di cui hai bisogno alla chars
stringa.
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);
}
per non perdere caratteri (+ / -) e se vuoi usare il tuo guid in un url, devi trasformarlo in base32
per 10000000 nessuna chiave duplicata
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;
}
Puoi provare con la seguente libreria:
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
restituisca un valore di tick univoco per ogni millisecondo: secondo le osservazioni , la risoluzione dipende dal timer di sistema. Inoltre, faresti meglio ad assicurarti che l'orologio di sistema non cambi all'indietro! (Anche la risposta di ur3an0 ha questi problemi.)
Puoi usare
code = await UserManager.GenerateChangePhoneNumberTokenAsync(input.UserId, input.MobileNumber);
solo i suoi 6
simpatici personaggi 599527
,143354
e quando l'utente lo virifica semplicemente
var result = await UserManager.VerifyChangePhoneNumberTokenAsync(input.UserId, input.Token, input.MobileNumber);
spero che questo ti aiuti
Guid.NewGuid().ToString().Split('-').First()
Io uso Guid.NewGuid().ToString().Split('-')[0]
, ottiene il primo elemento dall'array separato da "-". È sufficiente per rappresentare una chiave unica.