ユニークなランダム文字列の生成


97

たとえば、MSDNライブラリ(エラーオブジェクト)によって生成されるようなランダムな一意の文字列を生成したいのですが。't9zk6eay'のような文字列が生成されます。


1
これをstring randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);もっと試してみてください
shaijut

1
完全に一意になるものは、時間、場所などのランダムでないものに基づいている必要があるため、実際には完全にランダムになることはありません。GUIDはランダムに見えるかもしれませんが、実際にはそうではありません。IMOの唯一の希望は、それを非常にランダムで複雑にすることで、すべての実用的な目的で値が一意になります(つまり、衝突の可能性が非常に低くなります)。
bytedev 2018

回答:


84

Guidを使用するのはかなり良い方法ですが、例のようなものを取得するには、おそらくそれをBase64文字列に変換する必要があります。

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

「=」と「+」を取り除いて、例に少し近づけます。そうしないと、文字列の最後に「==」が入り、中央に「+」が入ります。次に出力文字列の例を示します。

「OZVV5TpP4U6wJthaCORZEQ」


15
/の置き換えも検討する必要があります。
Jason Kealey、2009年

20
シーケンスは推測できるため、Guidは安全なランダム文字列とは見なされません。Guidは、ランダムではなく、キーの競合を回避するために設計されています。スタックオーバーフローに関するGuidのランダム性について、いくつかの良い議論があります。
Daniel Bradley

何についての明確かつ簡潔な説明Convert.ToBase64Stringについては、こちらをご覧ください
jwaliszko

2
GUIDをbase64に変換し、+と=を置き換えることで、衝突確率を上げることはできますか?
Milan Aggarwal、2015年

5
@SimonEjsing new Guid()「ハッキング」(時計や内部Windowsデータ構造を改ざん)せずに使用すると衝突が発生するアプリケーションを実際に作成できる場合は、ビールに招待します。好きなだけコア、スレッド、同期プリミティブなどを自由に使用してください。
Lucero、2016年

175

2016/1/23アップデート

この回答が役に立った場合は、私が公開たシンプルな(〜500 SLOC)パスワード生成ライブラリに興味があるかもしれません。

Install-Package MlkPwgen

次に、以下の答えのようにランダムな文字列を生成できます。

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

ライブラリの利点の1つは、コードをより効率的に除外できるため、文字列を生成するだけではなく、安全なランダム性使用できることです。チェックアウトプロジェクトサイトの詳細については。

元の回答

安全なコードはまだ誰も提供していないので、誰かが便利だと思った場合に備えて、以下を投稿します。

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

.NET Coreでコードを機能させる方法を指摘してくれたAhmadに感謝します。


@Keltexソリューションはmehに対して正しく機能していませんでした(いくつかの使用後に同じ文字列を返していました)。このソリューションは完璧に機能します:)
JoanComasFdz

2
@LeeGrissom、バイアスは重要な側面です。たとえば、アルファベットに255文字が含まれていて、0〜255のランダムな値を取得するとします。リングバッファーでは、値0と255の両方が同じ文字に対応し、アルファベットの最初の文字を優先して結果を歪め、ランダム性が低くなります。これが問題になるかどうかは、もちろんアプリケーションによって異なります。
OskarSjöberg2015年

4
誰がターゲットだ.netcore:交換するvar rng = new RNGCryptoServiceProvider()var rng = RandomNumberGenerator.Create()
AMD

1
なぜ「var outOfRangeStart = byteSize-(byteSize%allowedCharSet.Length);」を計算するのですか?各反復について?「使用」する前に計算できます。
mtkachenko 2017

1
@BartCalixto修正済み。ありがとう!
マイケルクロパット

38

GUIDは乱数でないことに注意してください。完全にランダムであると予想されるものを生成するための基礎として使用しないでください(http://en.wikipedia.org/wiki/Globally_Unique_Identifierを参照):

WinAPI GUIDジェネレーターの暗号解析は、V4 GUIDのシーケンスが疑似ランダムであるため、初期状態が与えられると、関数UuidCreateによって返される次の250 000 GUIDまで予測できることを示しています。これが、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が細かいですユニーク(データベース内で一意のファイル名やキーなど)を、彼らはあなたがなりたいもののために良いものではありませんランダム(パスワードや暗号化キーなど)。したがって、アプリケーションによって異なります。

編集。マイクロソフトは、ランダムもそれほど良くないと言っています(http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx):

たとえば、ランダムパスワードの作成に適した暗号で保護された乱数を生成するには、System.Security.Cryptography.RNGCryptoServiceProviderなどのSystem.Security.Cryptography.RandomNumberGeneratorから派生したクラスを使用します。


5
C#ランダムクラスも「ランダム」ではなく、特定のシード番号から始まる古典的なランダムジェネレーターであるため、どの暗号コードにも適していません。同じシードは、同じ番号のシーケンスを返します。GUIDのアプローチは、ここではすでにはるかに優れています(「ランダム」ではなく「ユニーク」)。
Lucero、

3
@ルセロ:その通りです。マイクロソフトは、「ランダムパスワードの作成に適した暗号で保護された乱数を生成するには、たとえば、System.Security.Cryptography.RandomNumberGeneratorから派生したクラス(System.Security.Cryptography.RNGCryptoServiceProviderなど)を使用することをお勧めします。
Keltex 2009

さて、質問はすでに彼が(疑似)ランダムな一意の文字列を望んでいるので、暗号要件はなく、特定のランダム分布に従う必要さえないことを述べました。したがって、GUIDはおそらく最も簡単なアプローチです。
ジョーイ

1
「初期状態では次の250 000 GUIDまで予測できる」というステートメントは PRNGの本質的に真のステートメントのように思われます ...これも安全ではないと確信していますが、生成に多くの価値があるとは確信していません真にランダムなURL(OPが目的の場合)。;)
ojrac 2009

1
(とにかく+1-PRNG教育が重要です。)
ojrac 2009

13

@Michael Kropatsソリューションを簡略化し、LINQ風のバージョンを作成しました。

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

11

それらが本当にランダムであるとは思わないが、私はそれらがいくつかのハッシュであると思う。

ランダムな識別子が必要なときはいつでも、私は通常GUIDを使用して、それを「裸の」表現に変換します。

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

@Keltexが指摘したように、WinAPI GUIDジェネレーターの暗号解析は、V4 GUIDのシーケンスが疑似ランダムであるため、初期状態が与えられると、関数UuidCreateによって返される次の250 000 GUIDまで予測できることを示しています。
JoanComasFdz

4

GuidとTimeの組み合わせを試してください。

 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フィドルを参照してください。

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

VB.netのMichael Kropatsソリューション

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がすでに使用されているかどうかを確認するために、Dictionary <>またはデータベースのチェックも必要です。


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
  • マイクロソフトのリンクがランダムに生成されているかどうかわからない
  • 新しいGuid()。ToString()をご覧ください

4
あなたはGuid.NewGuid()を意味するのToString() - 。GUIDは、publicコンストラクタがない
日中韓

3
あなたはおそらく正しいです、検証なしでタイプしていました。元のポスターがポイントだと思います。
Fabian Vilers、2009

-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が2つ)、eaaea(3つのaと2つのe)、60207(2つの0)などの文字が繰り返されます。英数字の組み合わせで文字を繰り返さないランダム文字列を生成するにはどうすればよいですか?
ビジェイ2012年

@vijay:16進数を出力しているので、16文字と16文字に制限されています!可能な出力。ランダムな文字列はそれだけでランダムです。理論的には、すべての文字列(aaaaaaaaaaaaaaa)を取得できます。ありそうもないことですが、他のランダム文字列と同じです。なぜその制約が必要になるのかはわかりませんが、文字列に文字を追加するときに、それらをHashSet <T>にポップし、その存在を確認して、文字列に追加するか、それに応じてスキップします。
Chris Doggett
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.