การสร้างสตริงแบบสุ่มที่ไม่ซ้ำกัน


98

ฉันต้องการสร้างสตริงที่ไม่ซ้ำกันแบบสุ่มเช่นสตริงที่สร้างโดยไลบรารี MSDN ( Error Object ) ตัวอย่างเช่น ควรสร้างสตริงเช่น "t9zk6eay"


1
ลองstring randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);ดูเพิ่มเติมได้ที่นี่
shaijut

1
สำหรับบางสิ่งที่จะไม่ซ้ำกันโดยสิ้นเชิงนั้นจะต้องขึ้นอยู่กับสิ่งที่ไม่สุ่มเช่นเวลาสถานที่ ฯลฯ ดังนั้นจึงไม่สามารถสุ่มได้อย่างสมบูรณ์ Guid อาจดูเหมือนสุ่ม แต่ในความเป็นจริงแล้วไม่ใช่ IMO ความหวังเดียวของคุณคือทำให้มันสุ่มและซับซ้อนเพื่อวัตถุประสงค์ในทางปฏิบัติทั้งหมดค่าจะไม่ซ้ำกัน
bytedev

คำตอบ:


86

การใช้ Guid น่าจะเป็นวิธีที่ดี แต่เพื่อให้ได้สิ่งที่ดูเหมือนตัวอย่างของคุณคุณอาจต้องการแปลงเป็นสตริง Base64:

    Guid g = Guid.NewGuid();
    string GuidString = Convert.ToBase64String(g.ToByteArray());
    GuidString = GuidString.Replace("=","");
    GuidString = GuidString.Replace("+","");

ฉันกำจัด "=" และ "+" เพื่อเข้าใกล้ตัวอย่างของคุณอีกเล็กน้อยมิฉะนั้นคุณจะได้ "==" ที่ส่วนท้ายของสตริงและมี "+" ตรงกลาง นี่คือสตริงเอาต์พุตตัวอย่าง:

"OZVV5TpP4U6wJthaCORZEQ"


15
คุณควรพิจารณาเปลี่ยน / ด้วย
Jason Kealey

20
Guid ไม่ควรถือเป็นสตริงสุ่มที่ปลอดภัยเนื่องจากสามารถเดาลำดับได้ Guid ถูกออกแบบมาเพื่อหลีกเลี่ยงความขัดแย้งที่สำคัญแทนที่จะเป็นแบบสุ่ม มีการอภิปรายที่ดีเกี่ยวกับการสุ่มของ Guid around บน stack overflow
Daniel Bradley

สำหรับคำอธิบายที่ชัดเจนและระยะสั้นของสิ่งที่Convert.ToBase64Stringเป็นเรื่องเกี่ยวกับลองดูที่นี่
jwaliszko

2
สามารถแปลง guid เป็น base64 และแทนที่ + และ = เพิ่มความน่าจะเป็นในการชนได้หรือไม่?
Milan Aggarwal

7
@SimonEjsing ฉันจะเชิญคุณไปดื่มเบียร์หากคุณสามารถเขียนแอปพลิเคชันที่ทำให้เกิดการชนกันได้เมื่อใช้งานnew Guid()โดยไม่ต้อง "แฮ็ก" (การปลอมแปลงนาฬิกาหรือโครงสร้างข้อมูลภายในของ Windows) อย่าลังเลที่จะใช้คอร์เธรดไพรเมอร์ซิงโครไนซ์และอื่น ๆ ได้มากเท่าที่คุณต้องการ
Lucero

175

อัปเดต 2016/1/23

หากคุณพบว่าคำตอบนี้มีประโยชน์คุณอาจสนใจห้องสมุดการสร้างรหัสผ่านแบบธรรมดา (~ 500 SLOC) ที่ฉันเผยแพร่ :

Install-Package MlkPwgen

จากนั้นคุณสามารถสร้างสตริงแบบสุ่มได้เช่นเดียวกับในคำตอบด้านล่าง:

var str = PasswordGenerator.Generate(length: 10, allowed: Sets.Alphanumerics);

ข้อดีอย่างหนึ่งของห้องสมุดคือว่ารหัสเป็นปัจจัยที่ดีกว่าออกเพื่อให้คุณสามารถใช้การสุ่มที่เชื่อถือได้มากขึ้นกว่าการสร้างสตริง ตรวจสอบไซต์โครงการเพื่อดูรายละเอียดเพิ่มเติม

คำตอบเดิม

เนื่องจากยังไม่มีใครให้รหัสความปลอดภัยฉันจึงโพสต์สิ่งต่อไปนี้เผื่อว่าใครพบว่ามีประโยชน์

string RandomString(int length, string allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") {
    if (length < 0) throw new ArgumentOutOfRangeException("length", "length cannot be less than zero.");
    if (string.IsNullOrEmpty(allowedChars)) throw new ArgumentException("allowedChars may not be empty.");

    const int byteSize = 0x100;
    var allowedCharSet = new HashSet<char>(allowedChars).ToArray();
    if (byteSize < allowedCharSet.Length) throw new ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize));

    // Guid.NewGuid and System.Random are not particularly random. By using a
    // cryptographically-secure random number generator, the caller is always
    // protected, regardless of use.
    using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create()) {
        var result = new StringBuilder();
        var buf = new byte[128];
        while (result.Length < length) {
            rng.GetBytes(buf);
            for (var i = 0; i < buf.Length && result.Length < length; ++i) {
                // Divide the byte into allowedCharSet-sized groups. If the
                // random value falls into the last group and the last group is
                // too small to choose from the entire allowedCharSet, ignore
                // the value in order to avoid biasing the result.
                var outOfRangeStart = byteSize - (byteSize % allowedCharSet.Length);
                if (outOfRangeStart <= buf[i]) continue;
                result.Append(allowedCharSet[buf[i] % allowedCharSet.Length]);
            }
        }
        return result.ToString();
    }
}

ขอขอบคุณ Ahmad ที่ชี้ให้เห็นวิธีการทำให้โค้ดทำงานบน. NET Core


โซลูชัน @Keltex ไม่ทำงานสำหรับ meh (กำลังส่งคืนสตริงเดียวกันหลังจากใช้งานไม่กี่ครั้ง) โซลูชันนี้ทำงานได้อย่างสมบูรณ์แบบ :)
JoanComasFdz

2
@LeeGrissom การให้น้ำหนักเป็นสิ่งสำคัญ ยกตัวอย่างเช่นว่าตัวอักษรของคุณมี 255 อักขระและคุณจะได้ค่าสุ่มระหว่าง 0-255 ในวงแหวนบัฟเฟอร์ทั้งค่า 0 และ 255 จะสอดคล้องกับอักขระเดียวกันซึ่งจะทำให้ผลลัพธ์เอียงไปตามอักขระตัวแรกในตัวอักษรมันจะสุ่มน้อยลง ถ้าเรื่องนี้ขึ้นอยู่กับการใช้งานแน่นอน
Oskar Sjöberg

4
ใครกำหนดเป้าหมาย.netcore: แทนที่var rng = new RNGCryptoServiceProvider()ด้วยvar rng = RandomNumberGenerator.Create()
amd

1
ทำไมคุณถึงคำนวณ 'var outOfRangeStart = byteSize - (byteSize% allowCharSet.Length);' สำหรับการทำซ้ำแต่ละครั้ง? คุณสามารถคำนวณก่อน "ใช้"
mtkachenko

1
@BartCalixto คง. ขอบคุณ!
Michael Kropat

38

ฉันจะเตือนว่า guid ของมีไม่ได้ตัวเลขสุ่ม ไม่ควรใช้เป็นพื้นฐานในการสร้างสิ่งที่คุณคาดว่าจะสุ่มโดยสิ้นเชิง (ดูhttp://en.wikipedia.org/wiki/Globally_Unique_Identifier ):

Cryptanalysis ของตัวสร้าง WinAPI GUID แสดงให้เห็นว่าเนื่องจากลำดับของ V4 GUID เป็นแบบสุ่มหลอกเนื่องจากสถานะเริ่มต้นเราสามารถทำนายได้ถึง 250,000 GUID ถัดไปที่ส่งคืนโดยฟังก์ชัน UuidCreate นี่คือเหตุผลที่ไม่ควรใช้ GUID ในการเข้ารหัสเช่นเป็นคีย์แบบสุ่ม

ให้ใช้วิธีสุ่ม C # แทน สิ่งนี้ ( พบรหัสที่นี่ ):

private string RandomString(int size)
{
  StringBuilder builder = new StringBuilder();
  Random random = new Random();
  char ch ;
  for(int i=0; i<size; i++)
  {
    ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))) ;
    builder.Append(ch);
  }
  return builder.ToString();
}

GUID เป็นสิ่งที่ดีหากคุณต้องการสิ่งที่ไม่เหมือนใคร (เช่นชื่อไฟล์ที่ไม่ซ้ำกันหรือคีย์ในฐานข้อมูล) แต่ไม่ดีสำหรับบางสิ่งที่คุณต้องการสุ่ม (เช่นรหัสผ่านหรือคีย์เข้ารหัส) ขึ้นอยู่กับใบสมัครของคุณ

แก้ไข . Microsoft กล่าวว่า Random ก็ไม่ได้ยอดเยี่ยมเช่นกัน ( http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx ):

ในการสร้างหมายเลขสุ่มที่ปลอดภัยด้วยการเข้ารหัสที่เหมาะสมสำหรับการสร้างรหัสผ่านแบบสุ่มตัวอย่างเช่นใช้คลาสที่ได้มาจาก System.Security.Cryptography.RandomNumberGenerator เช่น System.Security.Cryptography.RNGCryptoServiceProvider


5
คลาสสุ่ม C # ไม่ใช่ "สุ่ม" อย่างใดอย่างหนึ่งและไม่เหมาะสำหรับรหัสการเข้ารหัสใด ๆ เนื่องจากเป็นเครื่องกำเนิดแบบสุ่มแบบคลาสสิกที่เริ่มต้นจากหมายเลขเมล็ดพันธุ์ที่เฉพาะเจาะจง เมล็ดพันธุ์เดียวกันจะส่งกลับลำดับตัวเลขที่ส่งคืนเหมือนกัน แนวทาง GUID นั้นดีกว่ามากอยู่แล้ว (ไม่ใช่ "สุ่ม" แต่เป็น "เฉพาะ")
Lucero

3
@ ลูเซโร: คุณถูกต้อง Microsoft แนะนำว่า "ในการสร้างหมายเลขสุ่มที่ปลอดภัยด้วยการเข้ารหัสที่เหมาะสมสำหรับการสร้างรหัสผ่านแบบสุ่มตัวอย่างเช่นให้ใช้คลาสที่ได้มาจาก System.Security.Cryptography.RandomNumberGenerator เช่น System.Security.Cryptography.RNGCryptoServiceProvider"
Keltex

คำถามได้ระบุไว้แล้วว่าเขาต้องการ (หลอก -) สตริงเฉพาะแบบสุ่มดังนั้นจึงไม่มีข้อกำหนดเกี่ยวกับการเข้ารหัสลับหรือแม้แต่ความจำเป็นในการติดตามการแจกแจงแบบสุ่มที่เฉพาะเจาะจง ดังนั้น GUID น่าจะเป็นแนวทางที่ง่ายที่สุด
Joey

1
ข้อความที่ว่า "กำหนดสถานะเริ่มต้นเราสามารถทำนายได้ถึง 250,000 GUID ถัดไป" ดูเหมือนจะเป็นคำสั่งที่แท้จริงสำหรับPRNG ใด ๆ ... ฉันแน่ใจว่ามันไม่ปลอดภัยเช่นกัน แต่ฉันไม่แน่ใจว่าจะมีคุณค่ามากมายในการสร้าง URL แบบสุ่มอย่างแท้จริงหากนั่นคือสิ่งที่ OP กำลังดำเนินไป ;)
ojrac

1
(+1 ต่อไป - การศึกษา PRNG เป็นสิ่งสำคัญ)
ojrac

13

ฉันทำให้โซลูชัน @Michael Kropats ง่ายขึ้นและสร้างเวอร์ชัน LINQ-esque

string RandomString(int length, string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
{       
    var outOfRange = byte.MaxValue + 1 - (byte.MaxValue + 1) % alphabet.Length;

    return string.Concat(
        Enumerable
            .Repeat(0, int.MaxValue)
            .Select(e => RandomByte())
            .Where(randomByte => randomByte < outOfRange)
            .Take(length)
            .Select(randomByte => alphabet[randomByte % alphabet.Length])
    );
}

byte RandomByte()
{
    using (var randomizationProvider = new RNGCryptoServiceProvider())
    {
        var randomBytes = new byte[1];
        randomizationProvider.GetBytes(randomBytes);
        return randomBytes.Single();
    }   
}

12

ฉันไม่คิดว่ามันเป็นแบบสุ่มจริงๆ แต่ฉันเดาว่ามันเป็นแฮช

เมื่อใดก็ตามที่ฉันต้องการตัวระบุแบบสุ่มฉันมักจะใช้ GUID และแปลงเป็นการแสดง "เปล่า":

Guid.NewGuid().ToString("n");

ดังที่ @Keltex ชี้ให้เห็น: Cryptanalysis ของตัวสร้าง WinAPI GUID แสดงให้เห็นว่าเนื่องจากลำดับของ V4 GUID เป็นแบบสุ่มหลอกเนื่องจากสถานะเริ่มต้นเราสามารถทำนายได้ถึง 250,000 GUID ถัดไปที่ส่งคืนโดยฟังก์ชัน UuidCreate
JoanComasFdz

4

ลองผสมผสานระหว่าง Guid และ Time.Ticks

 var randomNumber = Convert.ToBase64String(Guid.NewGuid().ToByteArray()) + DateTime.Now.Ticks;
     randomNumber = System.Text.RegularExpressions.Regex.Replace(randomNumber, "[^0-9a-zA-Z]+", "");

3

ฉันแปลกใจว่าทำไมไม่มีโซลูชัน CrytpoGraphic GUID จะไม่ซ้ำกัน แต่ไม่ปลอดภัยเข้ารหัส ดู Dotnet Fiddle นี้

var bytes = new byte[40]; // byte size
using (var crypto = new RNGCryptoServiceProvider())
  crypto.GetBytes(bytes);

var base64 = Convert.ToBase64String(bytes);
Console.WriteLine(base64);

ในกรณีที่คุณต้องการนำหน้าด้วย Guid:

var result = Guid.NewGuid().ToString("N") + base64;
Console.WriteLine(result);

สตริงตัวอักษรและตัวเลขที่สะอาดกว่า:

result = Regex.Replace(result,"[^A-Za-z0-9]","");
Console.WriteLine(result);

1

โซลูชัน Michael Kropats ใน VB.net

Private Function RandomString(ByVal length As Integer, Optional ByVal allowedChars As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") As String
    If length < 0 Then Throw New ArgumentOutOfRangeException("length", "length cannot be less than zero.")
    If String.IsNullOrEmpty(allowedChars) Then Throw New ArgumentException("allowedChars may not be empty.")


    Dim byteSize As Integer = 256
    Dim hash As HashSet(Of Char) = New HashSet(Of Char)(allowedChars)
    'Dim hash As HashSet(Of String) = New HashSet(Of String)(allowedChars)
    Dim allowedCharSet() = hash.ToArray

    If byteSize < allowedCharSet.Length Then Throw New ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize))


    ' Guid.NewGuid and System.Random are not particularly random. By using a
    ' cryptographically-secure random number generator, the caller is always
    ' protected, regardless of use.
    Dim rng = New System.Security.Cryptography.RNGCryptoServiceProvider()
    Dim result = New System.Text.StringBuilder()
    Dim buf = New Byte(128) {}
    While result.Length < length
        rng.GetBytes(buf)
        Dim i
        For i = 0 To buf.Length - 1 Step +1
            If result.Length >= length Then Exit For
            ' Divide the byte into allowedCharSet-sized groups. If the
            ' random value falls into the last group and the last group is
            ' too small to choose from the entire allowedCharSet, ignore
            ' the value in order to avoid biasing the result.
            Dim outOfRangeStart = byteSize - (byteSize Mod allowedCharSet.Length)
            If outOfRangeStart <= buf(i) Then
                Continue For
            End If
            result.Append(allowedCharSet(buf(i) Mod allowedCharSet.Length))
        Next
    End While
    Return result.ToString()
End Function

1

สิ่งนี้เหมาะสำหรับฉัน

    private string GeneratePasswordResetToken()
    {
        string token = Guid.NewGuid().ToString();
        var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(token);
        return Convert.ToBase64String(plainTextBytes);
    }

0

นี้ได้รับการขอภาษาต่างๆ นี่คือคำถามหนึ่งข้อเกี่ยวกับรหัสผ่านที่ควรใช้ที่นี่เช่นกัน

หากคุณต้องการใช้สตริงสำหรับการย่อ URL คุณจะต้องมีพจนานุกรม <> หรือฐานข้อมูลเพื่อตรวจสอบว่ามีการใช้ ID ที่สร้างขึ้นแล้วหรือไม่


0

หากคุณต้องการสตริงตัวอักษรและตัวเลขที่มีตัวพิมพ์เล็กและตัวอักษรที่ตัวพิมพ์ใหญ่ ([a-zA-Z0-9]) คุณสามารถใช้ Convert ToBase64String () สำหรับวิธีแก้ปัญหาที่รวดเร็วและง่ายดาย

สำหรับความเป็นเอกลักษณ์ให้ตรวจสอบปัญหาวันเกิดเพื่อคำนวณว่าจะมีการจัดเรียง (A) ความยาวของสตริงที่สร้างขึ้นและ (B) จำนวนสตริงที่สร้างขึ้น

Random random = new Random();

int outputLength = 10;
int byteLength = (int)Math.Ceiling(3f / 4f * outputLength); // Base64 uses 4 characters for every 3 bytes of data; so in random bytes we need only 3/4 of the desired length
byte[] randomBytes = new byte[byteLength];
string output;
do
{
    random.NextBytes(randomBytes); // Fill bytes with random data
    output = Convert.ToBase64String(randomBytes); // Convert to base64
    output = output.Substring(0, outputLength); // Truncate any superfluous characters and/or padding
} while (output.Contains('/') || output.Contains('+')); // Repeat if we contain non-alphanumeric characters (~25% chance if length=10; ~50% chance if length=20; ~35% chance if length=32)

-1
  • ไม่แน่ใจว่าลิงก์ของ Microsoft ถูกสร้างขึ้นแบบสุ่ม
  • ดู Guid ใหม่ (). ToString ()

4
คุณหมายถึง Guid.NewGuid () ToString () - Guid ไม่มีตัวสร้างสาธารณะ
cjk

3
คุณน่าจะพูดถูกกำลังพิมพ์โดยไม่มีการยืนยัน ฉันแน่ใจว่าโปสเตอร์ต้นฉบับมีประเด็น
Fabian Vilers

-1

รับคีย์เฉพาะโดยใช้รหัสแฮช GUID

public static string GetUniqueKey(int length)
{
    string guidResult = string.Empty;

    while (guidResult.Length < length)
    {
        // Get the GUID.
        guidResult += Guid.NewGuid().ToString().GetHashCode().ToString("x");
    }

    // Make sure length is valid.
    if (length <= 0 || length > guidResult.Length)
        throw new ArgumentException("Length must be between 1 and " + guidResult.Length);

    // Return the first length bytes.
    return guidResult.Substring(0, length);
}

ใช้งานได้อย่างสมบูรณ์แบบ แต่คำสุ่มไม่มีอักขระที่ไม่ซ้ำกัน อักขระซ้ำกันเช่น 114e3 (สอง 1), eaaea (สาม a และสอง e's), 60207 (0 สองตัว) เป็นต้น วิธีสร้างสตริงสุ่มโดยไม่มีการซ้ำอักขระด้วยการผสมตัวอักษรและตัวเลข?
วีเจย์

@vijay: เนื่องจากมันแสดงเลขฐานสิบหกคุณจึง จำกัด ตัวเองไว้ที่ 16 ตัวอักษรและ 16 ตัว! ผลลัพธ์ที่เป็นไปได้ สตริงสุ่มเป็นแบบสุ่ม ในทางทฤษฎีคุณสามารถหาสตริงของ a ทั้งหมดได้ (aaaaaaaaaaaaaaa) เป็นไปไม่ได้มาก แต่ไม่มากไปกว่าสตริงสุ่มอื่น ๆ ฉันไม่แน่ใจว่าทำไมคุณถึงต้องการข้อ จำกัด นั้น แต่ในขณะที่คุณกำลังเพิ่มอักขระลงในสตริงให้ป๊อปอัพใน HashSet <T> ตรวจสอบการมีอยู่และเพิ่มลงในสตริงหรือข้ามไป
Chris Doggett

-2

โซลูชันครบวงจรของฉันสำหรับคำสั่ง Linux บน windows คือสกู๊ป ติดตั้งสกู๊ปจากscoop.sh

scoop install openssl
openssl rand -base64 32
Dca3c3pptVkcb8fx243wN/3f/rQxx/rWYL8y7rZrGrA=

1
โหวตลงเพราะคำตอบนี้ไม่เกี่ยวข้องกับ C # อาจพบบ้านที่ดีกว่าสำหรับคำถามอื่น
Peter O.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.