.NET 짧은 고유 식별자


91

.NET에서 고유 식별자가 필요합니다 (이 경우 GUID가 너무 길기 때문에 사용할 수 없음).

사람들은 여기 에 사용 된 알고리즘 이 좋은 후보 라고 생각 합니까, 아니면 다른 제안이 있습니까?


3
얼마나 짧습니까? 그리고 얼마나 독특합니까? 이더넷 어댑터의 하드웨어 주소를 기반으로하는 경우 GUID는 고유 한 것으로 보장됩니다. 순전히 수학적으로 생산 된 모든 것은 절대로 독특 할 수 없습니다. (천문학적으로 높은 확률로) 아마도 독특 할 것입니다.
Jon

길이가 15이고 가능한 한 고유합니다 (아마도)
Noel

4
var random = 4; // 좋아요
KristoferA

4
(15) 어떤 길이? 15 바이트? 그렇다면 GUID에서 바이트를 제거하지 않는 이유는 무엇입니까 ..?
KristoferA

3
guid에서 바이트를 제거하면 천문학적으로 키 충돌 가능성이 높아집니다. 순서가 잘못된 바이트를 제거하면 충돌이 발생할 수 있습니다.
Chris Marisic

답변:


100

이것은 좋은 것입니다-http: //www.singular.co.nz/blog/archive/2007/12/20/shortguid-a-shorter-and-url-friendly-guid-in-c-sharp.aspx

여기에 YouTube와 같은 GUID

Base64를 사용할 수 있습니다.

string base64Guid = Convert.ToBase64String(Guid.NewGuid().ToByteArray());

그러면 E1HKfn68Pkms5zsZsvKONw ==와 같은 문자열이 생성됩니다. GUID는 항상 128 비트이므로 ==를 생략 할 수 있습니다. ==는 항상 끝에 존재하며 22 자 문자열을 제공합니다. 그래도 YouTube만큼 짧지는 않습니다.


11
짧은 메모 : 이것이 URL에 필요한 경우이를 사용하는 사람은 '+'및 '/'문자도 삭제하고 싶을 수 있습니다.
pootzko


1
Unity에서 UnnyNet에 대해 최대 23 자 길이의 고유 ID를 원했고, 멍청한 GUID에
갇혀서

흥미롭게도 (Zoom API로 작업하는 경우) 이것이 거의 확실하게 UUID를 생성하는 방법입니다. 길이는 22 자이며 base64로 인코딩됩니다.
vgel

39

Dor Cohen과 비슷한 접근 방식을 사용하지만 일부 특수 문자를 제거합니다.

var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");     

이것은 영숫자 문자 만 출력합니다. UID의 길이가 항상 동일하다고 보장 할 수는 없습니다. 다음은 샘플 실행입니다.

vmKo0zws8k28fR4V4Hgmw 
TKbhS0G2V0KqtpHOU8e6Ug 
rfDi1RdO0aQHTosh9dVvw
3jhCD75fUWjQek8XRmMg 
CQUg1lXIXkWG8KDFy7z6Ow 
bvyxW5aj10OmKA5KMhppw
pIMK8eq5kyvLK67xtsIDg
VX4oljGWpkSQGR2OvGoOQ 
NOHBjUUHv06yIc7EvotRg
iMniAuUG9kiGLwBtBQByfg

6
이와 같은 정보를 버리면 GUID가 보장하는 일부 속성이 손실됩니다. GUID와 직렬화 형식 사이의 이격을 유지하면서 익숙하지 않은 문자를 다른 문자로 바꾸는 것이 좋습니다.
Lukáš Lánský

3
이것은 GUID로 다시 변환하고 싶지만 다른 것을 위해 임의의 문자가 필요한 경우 사용하기에 좋은 방법입니다. 예를 들어 다중 스레드에서 작업자 추적 또는 스레드 된 개체에 대한 로깅 접두사 등
Piotr Kula

23
var ticks = new DateTime(2016,1,1).Ticks;
var ans = DateTime.Now.Ticks - ticks;
var uniqueId = ans.ToString("x");

이러한 ID 생성을 시작할 기준 날짜 (이 경우 2016 년 1 월 1 일)를 유지합니다. 이렇게하면 ID가 작아집니다.

생성 된 수 : 3af3c14996e54


milliseconds해당 DateTime개체에 대해 항상 0입니다
Teejay

마지막 문장도 제거하십시오.
Teejay

1
oneliner : var uniqueId = (DateTime.Now.Ticks-new DateTime (2016, 1, 1) .Ticks) .ToString ( "x");
Sgedda


17

간단하게 사용할 수있는 패키지. 임시 요청 ID 생성기에 사용합니다.

https://www.nuget.org/packages/shortid

https://github.com/bolorundurowb/shortid

용도 System.Random

string id = ShortId.Generate();
// id = KXTR_VzGVUoOY

(github 페이지에서)

숫자, 특수 문자 및 길이를 원하는지 여부를 지정하여 생성 된 ID 유형을 제어하려면 Generate 메서드를 호출하고 세 개의 매개 변수를 전달합니다. 첫 번째는 숫자를 원하는지 여부를 나타내는 부울, 두 번째는 원하는 여부를 나타내는 부울입니다. 특수 문자, 마지막은 선호하는 길이를 나타내는 숫자입니다.

string id = ShortId.Generate(true, false, 12);
// id = VvoCDPazES_w

10

내가 아는 한, GUID의 일부를 제거하는 것이 고유하다고 보장 할 수는 없습니다 . 사실 고유 한 것과는 거리가 멀습니다.

글로벌 고유성을 보장하는 가장 짧은 것은 Jeff Atwood의이 블로그 게시물에 나와 있습니다. 링크 된 게시물에서 그는 GUID를 줄이는 여러 방법에 대해 설명하고 결국 Ascii85 인코딩을 통해 20 바이트로 입니다.

그러나 15 바이트 이하의 솔루션이 절대적으로 필요한 경우 전역 적으로 고유하다고 보장되지 않는 것을 사용하는 것 외에 다른 선택의 여지가 없습니다.


6

IDENTITY 값은 데이터베이스에서 고유해야하지만 제한 사항을 알고 있어야합니다. 예를 들어 대량 데이터 삽입이 기본적으로 불가능 해져서 매우 많은 수의 레코드로 작업하는 경우 속도가 느려집니다.

날짜 / 시간 값을 사용할 수도 있습니다. 날짜 / 시간을 PK로 사용하는 데이터베이스를 여러 개 보았습니다. 매우 깨끗하지는 않지만 작동합니다. 삽입을 제어하면 값이 코드에서 고유하다는 것을 효과적으로 보장 할 수 있습니다.


6

내 로컬 앱의 경우이 시간 기반 접근 방식을 사용하고 있습니다.

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

3

여기 내 솔루션은 동시성, 초당 1000 GUID 및 스레드 안전에 안전하지 않습니다.

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

코드가 최적화되지 않았습니다. 샘플 만 있습니다!.


흥미로운 솔루션이지만 UtcNow밀리 초마다 고유 한 틱 값을 반환 한다는 보장은 없습니다 . 설명 에 따라 해상도는 시스템 타이머에 따라 다릅니다. 또한 시스템 시계가 뒤로 변하지 않는지 확인하는 것이 좋습니다! (user13971889의 답변이이 질문을 내 피드의 맨 위로 올렸고 그 답변을 비판했기 때문에 여기에서 그 비판을 반복해야한다고 생각합니다.)
Joe Sewell

3

앱에 몇 백만 명의 사용자가없는 경우 동일한 MILLISECOND에서 짧은 고유 문자열을 생성하는 것을 사용하면 아래 함수를 사용할 수 있습니다.

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

향후 99 년 동안 앱을 사용해야하는 경우 yyyy를 yy로 변경하십시오.
20160511 업데이트 : 임의 기능 수정
-잠금 개체 추가
-RandomString 함수 참조에서 임의 변수 이동


2
매번 새로운 Random을 초기화해서는 안되지만 이는 훌륭합니다. 그 이유 lock는 동일한 Random인스턴스 를 재사용 할 수 있기 때문입니다 . 그 줄을 지우는 걸 잊은 것 같아요!
NibblyPig

1

게시일과 상당히 멀다는 것을 알고 있습니다 ... :)

9 개의 Hexa 문자 만 생성하는 생성기가 있습니다. 예 : 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");
    }
}

1

@dorcohen의 답변과 @pootzko의 의견을 기반으로합니다. 이것을 사용할 수 있습니다. 와이어를 통해 안전합니다.

var errorId = System.Web.HttpServerUtility.UrlTokenEncode(Guid.NewGuid().ToByteArray());

궁금한 사람이있는 경우 결과 : Jzhw2oVozkSNa2IkyK4ilA2또는 dotnetfiddle.net/VIrZ8j
chriszo111

1

다른 일부를 기반으로 URL (및 Docker) 안전하고 정보를 잃지 않는 다른 인코딩 된 GUID를 제공하는 솔루션이 있습니다.

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("=", "").Replace("+", "-").Replace("/", "_");

출력 예는 다음과 같습니다.

BcfttHA780qMdHSxSBoZFA
_4p5srPgOE2f25T_UnoGLw
H9xR_zdfm0y-zYjdR3NOig

1

C #에서 long값은 64 비트이며 Base64로 인코딩 된 경우 패딩 1 개를 포함하여 12 개의 문자가 있습니다 =. 패딩을 다듬 으면 =11자가됩니다.

여기서 한 가지 미친 아이디어는 Unix Epoch와 하나의 Epoch 값에 대한 카운터를 조합하여 값을 형성 할 long수 있다는 것입니다. C #의 Unix Epoch DateTimeOffset.ToUnixEpochMillisecondslong 포맷이지만 그렇지 날짜 시간 값이 최대 날짜 시간 값보다 큰 것 때문에 8 바이트의 처음 2 바이트는 항상 0이다. 따라서 ushort카운터 를 배치 할 2 바이트가 됩니다.

따라서 전체적으로 ID 생성 횟수가 밀리 초당 65536 개를 초과하지 않는 한 고유 한 ID를 가질 수 있습니다.

// 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('=');

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

용법

Guid.NewGuid().ToTinyUuid()

다시 전환하는 것은 로켓 과학이 아니므로 그 정도는 남겨 두겠습니다.


0

문자열을 입력 할 필요가 없으면 다음을 사용할 수 있습니다.

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

이렇게하면 Guid가 다음과 같이 8 자 문자열로 변환됩니다.

{b77a49a5-182b-42fa-83a9-824ebd6ab58d}-> "䦥 띺 ᠫ 䋺 ꦃ 亂 檽 趵"

{c5f8f7f5-8a7c-4511-b667-8ad36b446617}-> " 엸 詼 䔑 架 펊 䑫 ᝦ"


0

다음은 임의의 짧은 고유 ID를 생성하는 작은 방법입니다. 안전한 난수 생성을 위해 암호화 rng를 사용합니다. chars문자열에 필요한 문자를 추가하십시오 .

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

0

문자 (+ /-)를 잃지 않고 URL에 GUID를 사용하려면 base32로 변환해야합니다.

10,000,000의 경우 중복 키 없음

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


-1
private static readonly object _getUniqueIdLock = new object();
public static string GetUniqueId()
{       
    lock(_getUniqueIdLock)
    {
        System.Threading.Thread.Sleep(1);
        return DateTime.UtcNow.Ticks.ToString("X");
    }
}

1
흥미로운 솔루션이지만 UtcNow밀리 초마다 고유 한 틱 값을 반환 한다는 보장은 없습니다 . 설명 에 따라 해상도는 시스템 타이머에 따라 다릅니다. 또한 시스템 시계가 뒤로 변하지 않는지 확인하는 것이 좋습니다! (ur3an0의 답변에도 이러한 문제가 있습니다.)
Joe Sewell

동의합니다. 이것은 가난한 사람의 접근 방식이며 자신의 잘 통제 된 환경을 넘어 사용해서는 안됩니다.
user13971889

-2

당신이 사용할 수있는

code = await UserManager.GenerateChangePhoneNumberTokenAsync(input.UserId, input.MobileNumber);

6좋은 문자 만 599527,143354

사용자가 간단하게 가상화하면

var result = await UserManager.VerifyChangePhoneNumberTokenAsync(input.UserId, input.Token, input.MobileNumber);

이것이 당신을 도울 수 있기를 바랍니다


나는 항상 암호를 간단하고 기억하기 쉽게 유지합니다.
Toolkit

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.