web-dev-qa-db-ja.com

ランダムな英数字の文字列を生成する方法

C#でランダムな8文字の英数字の文字列を生成する方法

813
KingNestor

私はLINQが新しい黒であると聞いたので、LINQを使った私の試みは次のとおりです。

private static Random random = new Random();
public static string RandomString(int length)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}

(注:Randomクラスを使用すると、パスワードやトークンを作成する場合など、このセキュリティ関連の問題には不向き _になります。強力な乱数ジェネレータが必要な場合はRNGCryptoServiceProviderクラスを使用してください。)

1474
dtb
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();

for (int i = 0; i < stringChars.Length; i++)
{
    stringChars[i] = chars[random.Next(chars.Length)];
}

var finalString = new String(stringChars);

Linqのソリューションほどエレガントではありません。

(注:Randomクラスを使用すると、パスワードやトークンを作成する場合など、このセキュリティ関連の問題には不向き _になります。強力な乱数ジェネレータが必要な場合はRNGCryptoServiceProviderクラスを使用してください。)

326
Dan Rigby

この実装(グーグルで見つけた)は私には健全に見えます。

提示されている他の選択肢とは異なり、これは 暗号的に正しい です。

using System.Security.Cryptography;
using System.Text;

namespace UniqueKey
{
    public class KeyGenerator
    {
        public static string GetUniqueKey(int size)
        {
            char[] chars =
                "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
            byte[] data = new byte[size];
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                crypto.GetBytes(data);
            }
            StringBuilder result = new StringBuilder(size);
            foreach (byte b in data)
            {
                result.Append(chars[b % (chars.Length)]);
            }
            return result.ToString();
        }
    }
}

選択肢の議論からそれを選びました ここ

291
Eric J.

解決策1 - 最も柔軟な長さを持つ最大の「範囲」

string get_unique_string(int string_length) {
    using(var rng = new RNGCryptoServiceProvider()) {
        var bit_count = (string_length * 6);
        var byte_count = ((bit_count + 7) / 8); // rounded up
        var bytes = new byte[byte_count];
        rng.GetBytes(bytes);
        return Convert.ToBase64String(bytes);
    }
}

GUIDは常に同じでランダムではない2つの固定ビットを持っているため、この解決法はGUIDを使用するよりも範囲が広く、たとえば16進数の13文字は常に " 4 " - 少なくともバージョン6のGUID。

このソリューションでは、任意の長さの文字列を生成することもできます。

解決策2 - 1行のコード - 最大22文字まで可能

Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);

解決策1 であり、GUIDの固定ビットのために文字列の範囲が同じでない限り、文字列を生成することはできませんが、多くの場合これでうまくいきます。

解決策3 - わずかに少ないコード

Guid.NewGuid().ToString("n").Substring(0, 8);

主に歴史的な目的のためにここにこれを保管してください。それはわずかに少ないコードを使用します、それはより少ない範囲を持つことの費用として来るけれども - それはbase64の代わりに16進数を使用するのでそれは他の解決策と比較して同じ範囲を表すためにより多くの文字を取ります。

これは衝突の可能性が高いことを意味します - 8文字の文字列を100,000回繰り返してテストすると、1つの複製が生成されます。

184
Douglas

これは、Sam Allenの例から Dot Net Perls から盗んだ例です。

8文字だけが必要な場合は、System.IO名前空間でPath.GetRandomFileName()を使用してください。 Samは、「ここではPath.GetRandomFileNameメソッドを使用するほうが優れています。ランダム性を高めるためにRNGCryptoServiceProviderを使用しているためです。ただし、11文字までに制限されています。」

GetRandomFileNameは常に9文字目にピリオドを含む12文字の文字列を返します。したがって、ピリオドを削除して(ランダムではないため)、文字列から8文字を取り出す必要があります。実際には、あなたはちょうど最初の8文字を取り、期間について心配することはできませんでした。

public string Get8CharacterRandomString()
{
    string path = Path.GetRandomFileName();
    path = path.Replace(".", ""); // Remove period.
    return path.Substring(0, 8);  // Return 8 character string
}

シモンズ:感謝サム

58
Adam Porad

私のコードの主な目的は以下のとおりです。

  1. 文字列の分布はほぼ一様です(小さい限り、小さな偏差は気にしないでください)。
  2. 各引数セットに対して数十億以上の文字列を出力します。 PRNGが20億(31ビットのエントロピー)の異なる値しか生成しない場合、8文字の文字列(〜47ビットのエントロピー)を生成しても意味がありません。
  3. 私は人々がこれをパスワードや他のセキュリティトークンに使うことを期待しているので安全です。

最初の特性は、アルファベットサイズを法とする64ビット値をとることによって達成されます。小さなアルファベット(質問の62文字など)の場合、これは無視できるバイアスになります。 2番目と3番目のプロパティは、 System.Random の代わりに RNGCryptoServiceProvider を使用することによって実現されます。

using System;
using System.Security.Cryptography;

public static string GetRandomAlphanumericString(int length)
{
    const string alphanumericCharacters =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        "abcdefghijklmnopqrstuvwxyz" +
        "0123456789";
    return GetRandomString(length, alphanumericCharacters);
}

public static string GetRandomString(int length, IEnumerable<char> characterSet)
{
    if (length < 0)
        throw new ArgumentException("length must not be negative", "length");
    if (length > int.MaxValue / 8) // 250 million chars ought to be enough for anybody
        throw new ArgumentException("length is too big", "length");
    if (characterSet == null)
        throw new ArgumentNullException("characterSet");
    var characterArray = characterSet.Distinct().ToArray();
    if (characterArray.Length == 0)
        throw new ArgumentException("characterSet must not be empty", "characterSet");

    var bytes = new byte[length * 8];
    var result = new char[length];
    using (var cryptoProvider = new RNGCryptoServiceProvider())
    {
        cryptoProvider.GetBytes(bytes);
    }
    for (int i = 0; i < length; i++)
    {
        ulong value = BitConverter.ToUInt64(bytes, i * 8);
        result[i] = characterArray[value % (uint)characterArray.Length];
    }
    return new string(result);
}
35
CodesInChaos

最も簡単な:

public static string GetRandomAlphaNumeric()
{
    return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}

Char配列をハードコーディングしてSystem.Randomに依存すると、パフォーマンスが向上します。

public static string GetRandomAlphaNumeric()
{
    var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}

英語のアルファベットが変更されてビジネスを失う可能性があることを心配している場合は、ハードコーディングを避けることができますが、パフォーマンスは少し悪くなります(Path.GetRandomFileNameアプローチに相当)。

public static string GetRandomAlphaNumeric()
{
    var chars = 'a'.To('z').Concat('0'.To('9')).ToList();
    return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}

public static IEnumerable<char> To(this char start, char end)
{
    if (end < start)
        throw new ArgumentOutOfRangeException("the end char should not be less than start char", innerException: null);
    return Enumerable.Range(start, end - start + 1).Select(i => (char)i);
}

最後の2つのアプローチは、それらをSystem.Randomインスタンスの拡張メソッドにすることができればより良く見えます。

29
nawfal

このスレッドのさまざまな答えのパフォーマンス比較をいくつか示します。

方法とセットアップ

// what's available
public static string possibleChars = "abcdefghijklmnopqrstuvwxyz";
// optimized (?) what's available
public static char[] possibleCharsArray = possibleChars.ToCharArray();
// optimized (precalculated) count
public static int possibleCharsAvailable = possibleChars.Length;
// shared randomization thingy
public static Random random = new Random();


// http://stackoverflow.com/a/1344242/1037948
public string LinqIsTheNewBlack(int num) {
    return new string(
    Enumerable.Repeat(possibleCharsArray, num)
              .Select(s => s[random.Next(s.Length)])
              .ToArray());
}

// http://stackoverflow.com/a/1344258/1037948
public string ForLoop(int num) {
    var result = new char[num];
    while(num-- > 0) {
        result[num] = possibleCharsArray[random.Next(possibleCharsAvailable)];
    }
    return new string(result);
}

public string ForLoopNonOptimized(int num) {
    var result = new char[num];
    while(num-- > 0) {
        result[num] = possibleChars[random.Next(possibleChars.Length)];
    }
    return new string(result);
}

public string Repeat(int num) {
    return new string(new char[num].Select(o => possibleCharsArray[random.Next(possibleCharsAvailable)]).ToArray());
}

// http://stackoverflow.com/a/1518495/1037948
public string GenerateRandomString(int num) {
  var rBytes = new byte[num];
  random.NextBytes(rBytes);
  var rName = new char[num];
  while(num-- > 0)
    rName[num] = possibleCharsArray[rBytes[num] % possibleCharsAvailable];
  return new string(rName);
}

//SecureFastRandom - or SolidSwiftRandom
static string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; 
    char[] rName = new char[Length];
    SolidSwiftRandom.GetNextBytesWithMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

結果

LinqPadでテスト済み。文字列サイズが10の場合は、次のように生成されます。

  • linq = chdgmevhcyより[10]
  • ループ= gtnoaryhxrから[10]
  • 選択= rsndbztyby [10]
  • generateRandomString = owyefjjakj [10]より
  • secureFastRandom = VzougLYHYP [10]より
  • secureFastRandom-NoCache = oVQXNGmO1S [10]より

パフォーマンスの数値はわずかに変動する傾向があります。ごくまれにNonOptimizedが実際には速くなることがあります。また、ForLoopGenerateRandomStringが誰をリードしているのかを切り替えることがあります。

  • LinqIsTheNewBlack(10000x)= 96762チック経過(9.6762ミリ秒)
  • ForLoop(10000x)= 28970ティック経過(2.897ミリ秒)
  • ForLoopNonOptimized(10000x)= 33336ティック経過(3.3336ミリ秒)
  • 繰り返し(10000x)= 78547チック経過(7.8547ミリ秒)
  • GenerateRandomString(10000x)= 27416ティック経過(2.7416ミリ秒)
  • SecureFastRandom(10000x)= 13176ティック経過(5ms)最短[別のマシン]
  • SecureFastRandom-NoCache(10000x)= 39541チック経過時間(17ミリ秒)最小[別のマシン]
20
drzaus

1行のコード Membership.GeneratePassword() でうまくいく:)

これが demo です。

17
Pooran

Eric J.によって書かれたコードはかなりずさんで(6年前のものであることは明らかです…彼はおそらく今日そのコードを書かないでしょう)、そしてさらにいくつかの問題があります。

提示されている他の選択肢とは異なり、これは暗号的に正しいものです。

Untrue ...(コメントに書かれているように)パスワードには偏りがあります、bcdefghは他のものより少し確からしいです(aGetNonZeroBytesによってゼロの値のバイトを生成しないからではありません、そのためaへの偏りはそれによってバランスがとられています)、それでそれは本当に暗号学的に健全ではありません。

これですべての問題が解決するはずです。

public static string GetUniqueKey(int size = 6, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
    using (var crypto = new RNGCryptoServiceProvider())
    {
        var data = new byte[size];

        // If chars.Length isn't a power of 2 then there is a bias if
        // we simply use the modulus operator. The first characters of
        // chars will be more probable than the last ones.

        // buffer used if we encounter an unusable random byte. We will
        // regenerate it in this buffer
        byte[] smallBuffer = null;

        // Maximum random number that can be used without introducing a
        // bias
        int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length);

        crypto.GetBytes(data);

        var result = new char[size];

        for (int i = 0; i < size; i++)
        {
            byte v = data[i];

            while (v > maxRandom)
            {
                if (smallBuffer == null)
                {
                    smallBuffer = new byte[1];
                }

                crypto.GetBytes(smallBuffer);
                v = smallBuffer[0];
            }

            result[i] = chars[v % chars.Length];
        }

        return new string(result);
    }
}
10
xanatos

質問: Enumerable.Rangeを入力する代わりに"ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789"を使用して時間を無駄にする必要があるのはなぜですか。

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    public static void Main()
    {
        var randomCharacters = GetRandomCharacters(8, true);
        Console.WriteLine(new string(randomCharacters.ToArray()));
    }

    private static List<char> getAvailableRandomCharacters(bool includeLowerCase)
    {
        var integers = Enumerable.Empty<int>();
        integers = integers.Concat(Enumerable.Range('A', 26));
        integers = integers.Concat(Enumerable.Range('0', 10));

        if ( includeLowerCase )
            integers = integers.Concat(Enumerable.Range('a', 26));

        return integers.Select(i => (char)i).ToList();
    }

    public static IEnumerable<char> GetRandomCharacters(int count, bool includeLowerCase)
    {
        var characters = getAvailableRandomCharacters(includeLowerCase);
        var random = new Random();
        var result = Enumerable.Range(0, count)
            .Select(_ => characters[random.Next(characters.Count)]);

        return result;
    }
}

答え: /魔法の弦は悪い。私の文字列の先頭に "I"がないことに誰もが気付きましたか?私の母は、まさにこの理由で魔法の弦を使わないように私に教えました...

n.b 1:@dtbのような多くの人が言ったように、暗号化セキュリティが必要な場合はSystem.Randomを使用しないでください。

n.b 2:この答えは最も効率的でも最短でもありませんが、答えと質問とを区別するためのスペースが必要でした。私の答えの目的は、空想的で革新的な答えを提供することではなく、魔法の弦に対して警告することです。

6
Wai Ha Lee

カスタム文字列のランダムも使用しますが、実装したのは文字列のヘルパーなので、ある程度の柔軟性があります。

public static string Random(this string chars, int length = 8)
{
    var randomString = new StringBuilder();
    var random = new Random();

    for (int i = 0; i < length; i++)
        randomString.Append(chars[random.Next(chars.Length)]);

    return randomString.ToString();
}

使用法

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();

または

var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);
6
Mr. Pumpkin

別の選択肢は、Linqを使ってランダムな文字を文字列ビルダーにまとめることです。

var chars = "abcdefghijklmnopqrstuvwxyz123456789".ToArray();
string pw = Enumerable.Range(0, passwordLength)
                      .Aggregate(
                          new StringBuilder(),
                          (sb, n) => sb.Append((chars[random.Next(chars.Length)])),
                          sb => sb.ToString());
6
AAD

私の簡単な一行コードは私のために働きます:)

string  random = string.Join("", Guid.NewGuid().ToString("n").Take(8).Select(o => o));

Response.Write(random.ToUpper());
Response.Write(random.ToLower());

これを任意の長さの文字列に展開する

    public static string RandomString(int length)
    {
        //length = length < 0 ? length * -1 : length;
        var str = "";

        do 
        {
            str += Guid.NewGuid().ToString().Replace("-", "");
        }

        while (length > str.Length);

        return str.Substring(0, length);
    }
6
Raj kumar

他の回答をレビューし、CodeInChaosのコメントを検討した後、CodeInChaosがまだ偏っている(より少ない)回答とともに、最終的な究極のカットアンドペーストソリューションは必要です。だから私の答えを更新している間、私はすべて出かけることにしました。

このコードの最新バージョンについては、Bitbucketの新しいHgリポジトリ https://bitbucket.org/merarischroeder/secureswiftrandom をご覧ください。次のコードをコピーして貼り付けることをお勧めします: https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer (Rawボタンをクリックしてコピーを簡単にし、最新バージョンがあることを確認してください。このリンクは、最新ではなく特定のバージョンのコードに移動すると思います)。

更新されたメモ:

  1. 他のいくつかの回答に関連する-出力の長さがわかっている場合、StringBuilderは必要ありません。ToCharArrayを使用する場合、これは配列を作成して入力します(最初に空の配列を作成する必要はありません)
  2. 他のいくつかの回答に関連して-パフォーマンスのために1つずつ取得するのではなく、NextBytesを使用する必要があります
  3. 技術的には、アクセスを高速化するためにバイト配列を固定することができます。通常、バイト配列で6〜8回以上反復する場合は価値があります。 (ここでは行われません)
  4. 最適なランダム性のためのRNGCryptoServiceProviderの使用
  5. ランダムデータの1MBバッファーのキャッシングの使用-ベンチマークでは、キャッシュされたシングルバイトのアクセス速度が〜1000倍高速であることが示されています-1MBで9ms、非キャッシュでは989msです。
  6. 新しいクラス内のバイアスゾーンの最適化された拒否

質問に対する最終解決策:

static char[] charSet =  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
public string GenerateRandomString(int Length) //Configurable output string length
{
    byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
    char[] rName = new char[Length];
    SecureFastRandom.GetNextBytesMax(rBytes, biasZone);
    for (var i = 0; i < Length; i++)
    {
        rName[i] = charSet[rBytes[i] % charSet.Length];
    }
    return new string(rName);
}

ただし、新しい(テストされていない)クラスが必要です。

/// <summary>
/// My benchmarking showed that for RNGCryptoServiceProvider:
/// 1. There is negligable benefit of sharing RNGCryptoServiceProvider object reference 
/// 2. Initial GetBytes takes 2ms, and an initial read of 1MB takes 3ms (starting to rise, but still negligable)
/// 2. Cached is ~1000x faster for single byte at a time - taking 9ms over 1MB vs 989ms for uncached
/// </summary>
class SecureFastRandom
{
    static byte[] byteCache = new byte[1000000]; //My benchmark showed that an initial read takes 2ms, and an initial read of this size takes 3ms (starting to raise)
    static int lastPosition = 0;
    static int remaining = 0;

    /// <summary>
    /// Static direct uncached access to the RNGCryptoServiceProvider GetBytes function
    /// </summary>
    /// <param name="buffer"></param>
    public static void DirectGetBytes(byte[] buffer)
    {
        using (var r = new RNGCryptoServiceProvider())
        {
            r.GetBytes(buffer);
        }
    }

    /// <summary>
    /// Main expected method to be called by user. Underlying random data is cached from RNGCryptoServiceProvider for best performance
    /// </summary>
    /// <param name="buffer"></param>
    public static void GetBytes(byte[] buffer)
    {
        if (buffer.Length > byteCache.Length)
        {
            DirectGetBytes(buffer);
            return;
        }

        lock (byteCache)
        {
            if (buffer.Length > remaining)
            {
                DirectGetBytes(byteCache);
                lastPosition = 0;
                remaining = byteCache.Length;
            }

            Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
            lastPosition += buffer.Length;
            remaining -= buffer.Length;
        }
    }

    /// <summary>
    /// Return a single byte from the cache of random data.
    /// </summary>
    /// <returns></returns>
    public static byte GetByte()
    {
        lock (byteCache)
        {
            return UnsafeGetByte();
        }
    }

    /// <summary>
    /// Shared with public GetByte and GetBytesWithMax, and not locked to reduce lock/unlocking in loops. Must be called within lock of byteCache.
    /// </summary>
    /// <returns></returns>
    static byte UnsafeGetByte()
    {
        if (1 > remaining)
        {
            DirectGetBytes(byteCache);
            lastPosition = 0;
            remaining = byteCache.Length;
        }

        lastPosition++;
        remaining--;
        return byteCache[lastPosition - 1];
    }

    /// <summary>
    /// Rejects bytes which are equal to or greater than max. This is useful for ensuring there is no bias when you are modulating with a non power of 2 number.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    public static void GetBytesWithMax(byte[] buffer, byte max)
    {
        if (buffer.Length > byteCache.Length / 2) //No point caching for larger sizes
        {
            DirectGetBytes(buffer);

            lock (byteCache)
            {
                UnsafeCheckBytesMax(buffer, max);
            }
        }
        else
        {
            lock (byteCache)
            {
                if (buffer.Length > remaining) //Recache if not enough remaining, discarding remaining - too much work to join two blocks
                    DirectGetBytes(byteCache);

                Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
                lastPosition += buffer.Length;
                remaining -= buffer.Length;

                UnsafeCheckBytesMax(buffer, max);
            }
        }
    }

    /// <summary>
    /// Checks buffer for bytes equal and above max. Must be called within lock of byteCache.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="max"></param>
    static void UnsafeCheckBytesMax(byte[] buffer, byte max)
    {
        for (int i = 0; i < buffer.Length; i++)
        {
            while (buffer[i] >= max)
                buffer[i] = UnsafeGetByte(); //Replace all bytes which are equal or above max
        }
    }
}

歴史のために-この答えの私の古い解決策は、ランダムオブジェクトを使用しました:

    private static char[] charSet =
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();

    static rGen = new Random(); //Must share, because the clock seed only has Ticks (~10ms) resolution, yet lock has only 20-50ns delay.
    static int byteSize = 256; //Labelling convenience
    static int biasZone = byteSize - (byteSize % charSet.Length);
    static bool SlightlyMoreSecurityNeeded = true; //Configuration - needs to be true, if more security is desired and if charSet.Length is not divisible by 2^X.
    public string GenerateRandomString(int Length) //Configurable output string length
    {
      byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
      char[] rName = new char[Length];
      lock (rGen) //~20-50ns
      {
          rGen.NextBytes(rBytes);

          for (int i = 0; i < Length; i++)
          {
              while (SlightlyMoreSecurityNeeded && rBytes[i] >= biasZone) //Secure against 1/5 increased bias of index[0-7] values against others. Note: Must exclude where it == biasZone (that is >=), otherwise there's still a bias on index 0.
                  rBytes[i] = rGen.NextByte();
              rName[i] = charSet[rBytes[i] % charSet.Length];
          }
      }
      return new string(rName);
    }

性能:

  1. SecureFastRandom-最初のシングルラン=〜9-33ms。気づかない。 進行中5ms(場合によっては最大13msまで)10,000回の反復、単一の平均反復で=1.5マイクロ秒注:通常は2回必要ですが、場合によっては最大8回のキャッシュリフレッシュが必要です-バイアスゾーンを超えるシングルバイト数によって異なります
  2. ランダム-最初のシングルラン=〜0-1ms。気づかない。 Ongoing5ms10,000回以上の繰り返し。単一の平均反復=。5マイクロ秒。。ほぼ同じ速度。

また、チェックアウト:

これらのリンクは別のアプローチです。この新しいコードベースにバッファリングを追加できますが、最も重要なのは、バイアスを除去するためのさまざまなアプローチを検討し、速度と賛否両論をベンチマークすることでした。

5

DTBのソリューションを少しきれいにしたものです。

    var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    var random = new Random();
    var list = Enumerable.Repeat(0, 8).Select(x=>chars[random.Next(chars.Length)]);
    return string.Join("", list);

あなたのスタイルの好みは変わるかもしれません。

5
Rob Deary

私はより具体的な答えを探していました、そこでは私がランダムな文字列のフォーマットを制御したいのですが、この記事に出くわしました。たとえば、(自動車の)ナンバープレートは(国ごとに)特定のフォーマットを持っており、ランダムなナンバープレートを作成したいと思いました。
私はこれのために私自身のランダム拡張方法を書くことにしました。 (これは、マルチスレッドのシナリオでは2倍になる可能性があるため、同じRandomオブジェクトを再利用するためです)。要旨( https://Gist.github.com/SamVanhoutte/808845ca78b9c041e928 )を作成しましたが、ここでも拡張クラスをコピーします。

void Main()
{
    Random rnd = new Random();
    rnd.GetString("1-###-000").Dump();
}

public static class RandomExtensions
{
    public static string GetString(this Random random, string format)
    {
        // Based on http://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings-in-c
        // Added logic to specify the format of the random string (# will be random string, 0 will be random numeric, other characters remain)
        StringBuilder result = new StringBuilder();
        for(int formatIndex = 0; formatIndex < format.Length ; formatIndex++)
        {
            switch(format.ToUpper()[formatIndex])
            {
                case '0': result.Append(getRandomNumeric(random)); break;
                case '#': result.Append(getRandomCharacter(random)); break;
                default : result.Append(format[formatIndex]); break;
            }
        }
        return result.ToString();
    }

    private static char getRandomCharacter(Random random)
    {
        string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        return chars[random.Next(chars.Length)];
    }

    private static char getRandomNumeric(Random random)
    {
        string nums = "0123456789";
        return nums[random.Next(nums.Length)];
    }
}
4
Sam Vanhoutte

恐ろしい、私は知っている、しかし私はただ自分自身を助けることができなかった:


namespace ConsoleApplication2
{
    using System;
    using System.Text.RegularExpressions;

    class Program
    {
        static void Main(string[] args)
        {
            Random adomRng = new Random();
            string rndString = string.Empty;
            char c;

            for (int i = 0; i < 8; i++)
            {
                while (!Regex.IsMatch((c=Convert.ToChar(adomRng.Next(48,128))).ToString(), "[A-Za-z0-9]"));
                rndString += c;
            }

            Console.WriteLine(rndString + Environment.NewLine);
        }
    }
}
4
james
 public static string RandomString(int length)
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var random = new Random();
        return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
    }
4
Tejas

2つの部分を組み合わせるようにしてください。

public class RandomStringGenerator
{
    public static string Gen()
    {
        return ConvertToBase(DateTime.UtcNow.ToFileTimeUtc()) + GenRandomStrings(5); //keep length fixed at least of one part
    }

    private static string GenRandomStrings(int strLen)
    {
        var result = string.Empty;

        var Gen = new RNGCryptoServiceProvider();
        var data = new byte[1];

        while (result.Length < strLen)
        {
            Gen.GetNonZeroBytes(data);
            int code = data[0];
            if (code > 48 && code < 57 || // 0-9
                code > 65 && code < 90 || // A-Z
                code > 97 && code < 122   // a-z
                )
            {
                result += Convert.ToChar(code);
            }
        }

        return result;
    }

    private static string ConvertToBase(long num, int nbase = 36)
    {
        var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //if you wish make algoritm more secure - change order of letter here

        // check if we can convert to another base
        if (nbase < 2 || nbase > chars.Length)
            return null;

        int r;
        var newNumber = string.Empty;

        // in r we have the offset of the char that was converted to the new base
        while (num >= nbase)
        {
            r = (int) (num % nbase);
            newNumber = chars[r] + newNumber;
            num = num / nbase;
        }
        // the last number to convert
        newNumber = chars[(int)num] + newNumber;

        return newNumber;
    }
}

テスト:

[Test]
    public void Generator_Should_BeUnigue1()
    {
        //Given
        var loop = Enumerable.Range(0, 1000);
        //When
        var str = loop.Select(x=> RandomStringGenerator.Gen());
        //Then
        var distinct = str.Distinct();
        Assert.AreEqual(loop.Count(),distinct.Count()); // Or Assert.IsTrue(distinct.Count() < 0.95 * loop.Count())
    }
3
RouR

ワンライナー風味になりました。

private string RandomName
    {
        get
        {
            return new string(
                Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                    .Select(s =>
                    {
                        var cryptoResult = new byte[4];
                        using (var cryptoProvider = new RNGCryptoServiceProvider())
                            cryptoProvider.GetBytes(cryptoResult);
                        return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                    })
                    .ToArray());
        }
    }
2

これは、Eric JのWinRT(Windows Store App)向けのソリューション、つまり暗号化されたソリューションの変形です。

public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new StringBuilder(length);
    for (int i = 0; i < length; ++i)
    {
        result.Append(CryptographicBuffer.GenerateRandomNumber() % chars.Length);
    }
    return result.ToString();
}

パフォーマンスが重要な場合(特に長さが長い場合):

public static string GenerateRandomString(int length)
{
    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    var result = new System.Text.StringBuilder(length);
    var bytes = CryptographicBuffer.GenerateRandom((uint)length * 4).ToArray();
    for (int i = 0; i < bytes.Length; i += 4)
    {
        result.Append(BitConverter.ToUInt32(bytes, i) % chars.Length);
    }
    return result.ToString();
}
2
huyc

Randomを使用しない解決策:

var chars = Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8);

var randomStr = new string(chars.SelectMany(str => str)
                                .OrderBy(c => Guid.NewGuid())
                                .Take(8).ToArray());
2
w.b

これがどれほど暗号的に正しいかわかりませんが、はるかに複雑なソリューション(imo)よりも読みやすくて簡潔で、System.Randomベースのソリューションよりも「ランダム」になるはずです。

return alphabet
    .OrderBy(c => Guid.NewGuid())
    .Take(strLength)
    .Aggregate(
        new StringBuilder(),
        (builder, c) => builder.Append(c))
    .ToString();

私は、このバージョンと次のバージョンのどちらが「きれい」だと思うのか判断できませんが、まったく同じ結果になります。

return new string(alphabet
    .OrderBy(o => Guid.NewGuid())
    .Take(strLength)
    .ToArray());

確かに速度には最適化されていませんので、毎秒数百万ものランダムな文字列を生成することがミッションクリティカルな場合は、もう1つ試してみてください。

注:この解決策はアルファベットのシンボルの繰り返しを許さず、アルファベットは出力文字列と同じかそれ以上のサイズでなければならず、状況によってはこのアプローチは望ましくないものになります。すべてあなたのユースケースに依存します。

1
sara

私はこれが最善の方法ではないことを知っています。しかし、あなたはこれを試すことができます。

string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
1
Sagar

あなたの値が完全にランダムではないが、実際には何かに依存するかもしれません - あなたはその「なんでも」のmd5またはsha1ハッシュを計算し、それからあなたが望むどんな長さにでもそれを切り捨てるかもしれません。

また、guidを生成して切り捨てることもできます。

0
Alexey B.

これは、アルファベットと数字を定義せずにランダムな英数字の文字列を生成するメカニズムです(私はこれを使用してパスワードとテストデータを生成します)。

CleanupBase64は文字列の必要な部分を削除し、ランダムな英数字を再帰的に追加し続けます。

        public static string GenerateRandomString(int length)
        {
            var numArray = new byte[length];
            new RNGCryptoServiceProvider().GetBytes(numArray);
            return CleanUpBase64String(Convert.ToBase64String(numArray), length);
        }

        private static string CleanUpBase64String(string input, int maxLength)
        {
            input = input.Replace("-", "");
            input = input.Replace("=", "");
            input = input.Replace("/", "");
            input = input.Replace("+", "");
            input = input.Replace(" ", "");
            while (input.Length < maxLength)
                input = input + GenerateRandomString(maxLength);
            return input.Length <= maxLength ?
                input.ToUpper() : //In my case I want capital letters
                input.ToUpper().Substring(0, maxLength);
        }
0
Dhanuka777
public static class StringHelper
{
    private static readonly Random random = new Random();

    private const int randomSymbolsDefaultCount = 8;
    private const string availableChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    private static int randomSymbolsIndex = 0;

    public static string GetRandomSymbols()
    {
        return GetRandomSymbols(randomSymbolsDefaultCount);
    }

    public static string GetRandomSymbols(int count)
    {
        var index = randomSymbolsIndex;
        var result = new string(
            Enumerable.Repeat(availableChars, count)
                      .Select(s => {
                          index += random.Next(s.Length);
                          if (index >= s.Length)
                              index -= s.Length;
                          return s[index];
                      })
                      .ToArray());
        randomSymbolsIndex = index;
        return result;
    }
}
0
KregHEk