Wie kann ich eine zufällige 8 Zeichen alphanumerische Zeichenfolge in C# generieren?
Antworten
Zu viele Anzeigen?Ich habe gehört, dass LINQ das neue Schwarz ist, also hier ist mein Versuch mit 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());
}
(Hinweis: Die Verwendung des Random
Klasse macht dies ungeeignet für sicherheitsrelevante Themen wie die Erstellung von Passwörtern oder Token. Verwenden Sie die RNGCryptoServiceProvider
Klasse, wenn Sie einen starken Zufallszahlengenerator benötigen).
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);
Nicht so elegant wie die Linq-Lösung.
(Hinweis: Die Verwendung des Random
Klasse macht dies ungeeignet für sicherheitsrelevante Themen wie die Erstellung von Passwörtern oder Token. Verwenden Sie die RNGCryptoServiceProvider
Klasse, wenn Sie einen starken Zufallszahlengenerator benötigen).
AKTUALISIERT für .NET 6. RNGCryptoServiceProvider ist als veraltet gekennzeichnet. Rufen Sie stattdessen RandomNumberGenerator.Create() . Der Code in der Antwort wurde entsprechend aktualisiert.
Aktualisiert aufgrund von Kommentaren. Die ursprüngliche Implementierung erzeugte in ~1,95% der Fälle a-h und in ~1,56% der Fälle die übrigen Zeichen. Die Aktualisierung erzeugt alle Zeichen in ~1,61 % der Zeit.
FRAMEWORK SUPPORT - .NET Core 3 (und zukünftige Plattformen, die den .NET Standard 2.1 oder höher unterstützen) bietet eine kryptografisch solide Methode RandomNumberGenerator.GetInt32() um eine zufällige ganze Zahl innerhalb eines gewünschten Bereichs zu erzeugen.
Im Gegensatz zu einigen der vorgestellten Alternativen ist diese Alternative kryptographisch einwandfrei .
using System;
using System.Security.Cryptography;
using System.Text;
namespace UniqueKey
{
public class KeyGenerator
{
internal static readonly char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
public static string GetUniqueKey(int size)
{
byte[] data = new byte[4*size];
using (var crypto = RandomNumberGenerator.Create())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
{
var rnd = BitConverter.ToUInt32(data, i * 4);
var idx = rnd % chars.Length;
result.Append(chars[idx]);
}
return result.ToString();
}
public static string GetUniqueKeyOriginal_BIASED(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();
}
}
}
Auf der Grundlage einer Diskussion von Alternativen ici und auf der Grundlage der nachstehenden Kommentare aktualisiert/geändert.
Hier ist ein kleines Testprogramm, das die Verteilung der Zeichen in der alten und aktualisierten Ausgabe demonstriert. Für eine ausführliche Diskussion über die Analyse der Zufälligkeit finden Sie unter random.org.
using System;
using System.Collections.Generic;
using System.Linq;
using UniqueKey;
namespace CryptoRNGDemo
{
class Program
{
const int REPETITIONS = 1000000;
const int KEY_SIZE = 32;
static void Main(string[] args)
{
Console.WriteLine("Original BIASED implementation");
PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKeyOriginal_BIASED);
Console.WriteLine("Updated implementation");
PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKey);
Console.ReadKey();
}
static void PerformTest(int repetitions, int keySize, Func<int, string> generator)
{
Dictionary<char, int> counts = new Dictionary<char, int>();
foreach (var ch in UniqueKey.KeyGenerator.chars) counts.Add(ch, 0);
for (int i = 0; i < REPETITIONS; i++)
{
var key = generator(KEY_SIZE);
foreach (var ch in key) counts[ch]++;
}
int totalChars = counts.Values.Sum();
foreach (var ch in UniqueKey.KeyGenerator.chars)
{
Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");
}
}
}
}
Aktualisierung 25.7.2022
Aufgrund einer Frage in den Kommentaren war ich neugierig, ob die Verteilung wirklich zufällig ist.
Ich bin kein Statistiker, aber ich könnte wahrscheinlich einen im Fernsehen spielen. Wenn sich ein tatsächlicher Statistiker einmischen möchte, wäre das sehr willkommen.
Es gibt 62 mögliche Ausgabewerte (A-Za-Z0-9) und int.MaxValue
Zahlen, die zur Auswahl eines Array-Index verwendet werden. int.MaxValue % 62
ist 1, d. h. ein Zeichen wird 1:4 Milliarden Mal häufiger ausgewählt als die anderen. Wir könnten diese Auswahlverzerrung weiter reduzieren, indem wir das Array der Ausgabewerte vor der Indizierung zufällig drehen.
Ein T-Test oder ein anderes statistisches Maß wäre der beste Ansatz, um festzustellen, ob die Ausgabeergebnisse verzerrt sind, aber das kann ich in meiner Mittagspause nicht durchführen, also überlasse ich Ihnen stattdessen eine Änderung des obigen Codes, der die Abweichung von der Erwartung misst. Beachten Sie, dass sie gegen Null tendiert.
using System.Security.Cryptography;
using System.Text;
const int REPETITIONS = 1_000_000;
const int KEY_SIZE = 32;
int TASK_COUNT = Environment.ProcessorCount - 1;
var expectedPercentage = 100.0 / KeyGenerator.chars.Length;
var done = false;
var iterationNr = 1;
var totalRandomSymbols = 0L;
var grandTotalCounts = new Dictionary<char, long>();
foreach (var ch in KeyGenerator.chars) grandTotalCounts.Add(ch, 0);
while (!done)
{
var experiments = Enumerable.Range(0, TASK_COUNT).Select(i => Task.Run(Experiment)).ToArray();
Task.WaitAll(experiments);
var totalCountsThisRun = experiments.SelectMany(e => e.Result)
.GroupBy(e => e.Key)
.Select(e => new { e.Key, Count = e.Select(_ => _.Value).Sum() })
.ToDictionary(e => e.Key, e => e.Count);
foreach (var ch in KeyGenerator.chars)
grandTotalCounts[ch] += totalCountsThisRun[ch];
var totalChars = grandTotalCounts.Values.Sum();
totalRandomSymbols += totalChars;
var distributionScores = KeyGenerator.chars.Select(ch =>
new
{
Symbol = ch,
OverUnder = (100.0 * grandTotalCounts[ch] / totalChars) - expectedPercentage
});
Console.WriteLine($"Iteration {iterationNr++}. Total random symbols: {totalRandomSymbols:N0}");
foreach (var chWithValue in distributionScores.OrderByDescending(c => c.OverUnder))
{
Console.WriteLine($"{chWithValue.Symbol}: {chWithValue.OverUnder:#.00000}%");
}
done = Console.KeyAvailable;
}
Dictionary<char, long> Experiment()
{
var counts = new Dictionary<char, long>();
foreach (var ch in KeyGenerator.chars) counts.Add(ch, 0);
for (int i = 0; i < REPETITIONS; i++)
{
var key = KeyGenerator.GetUniqueKey(KEY_SIZE);
foreach (var ch in key) counts[ch]++;
}
return counts;
}
public class KeyGenerator
{
internal static readonly char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
public static string GetUniqueKey(int size)
{
byte[] data = new byte[4 * size];
using (var crypto = RandomNumberGenerator.Create())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
{
var rnd = BitConverter.ToUInt32(data, i * 4);
var idx = rnd % chars.Length;
result.Append(chars[idx]);
}
return result.ToString();
}
}
Lösung 1 - größte "Reichweite" mit flexibelster Länge
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);
}
}
Diese Lösung hat eine größere Reichweite als die Verwendung einer GUID, da eine GUID einige feste Bits hat, die immer gleich sind und daher nicht zufällig sind, z. B. ist das 13-Zeichen in Hex immer "4" - zumindest in einer GUID der Version 6.
Mit dieser Lösung können Sie auch eine Zeichenkette beliebiger Länge erzeugen.
Lösung 2 - Eine Codezeile - für bis zu 22 Zeichen
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);
Sie können keine Zeichenketten erzeugen, die so lang sind wie Lösung 1 und die Zeichenkette hat nicht den gleichen Bereich aufgrund fester Bits in GUIDs, aber in vielen Fällen wird dies die Aufgabe erfüllen.
Lösung 3 - Etwas weniger Code
Guid.NewGuid().ToString("n").Substring(0, 8);
Ich behalte das hier vor allem aus historischen Gründen. Es verwendet etwas weniger Code, das kommt aber auf Kosten von weniger Reichweite - weil es Hex anstelle von Base64 verwendet, braucht es mehr Zeichen, um den gleichen Bereich im Vergleich zu den anderen Lösungen zu repräsentieren.
Das bedeutet, dass die Wahrscheinlichkeit von Kollisionen größer ist - beim Testen mit 100.000 Iterationen von 8-Zeichen-Strings wurde ein Duplikat erzeugt.
Hier ist ein Beispiel, das ich von Sam Allen geklaut habe. Dot Net Perls
Wenn Sie nur 8 Zeichen benötigen, dann verwenden Sie Path.GetRandomFileName() im System.IO-Namensraum. Sam sagt, dass die "Path.GetRandomFileName-Methode hier manchmal besser ist, weil sie RNGCryptoServiceProvider für eine bessere Zufälligkeit verwendet. Allerdings ist sie auf 11 Zufallszeichen begrenzt."
GetRandomFileName gibt immer eine 12-stellige Zeichenkette mit einem Punkt am 9. Zeichen zurück. Sie müssen also den Punkt entfernen (da dieser nicht zufällig ist) und dann 8 Zeichen aus der Zeichenfolge nehmen. Eigentlich könnten Sie auch nur die ersten 8 Zeichen nehmen und sich nicht um den Punkt kümmern.
public string Get8CharacterRandomString()
{
string path = Path.GetRandomFileName();
path = path.Replace(".", ""); // Remove period.
return path.Substring(0, 8); // Return 8 character string
}
PS: Danke Sam
- See previous answers
- Weitere Antworten anzeigen
2 Stimmen
Welcher Rest
8 Stimmen
O s o s
2 Stimmen
Es wäre schön, wenn die Sprachlokalisierung in diese Frage einbezogen würde. Vor allem, wenn Ihre Benutzeroberfläche für Chinesisch oder Bulgarisch geeignet sein soll!
0 Stimmen
Siehe auch Wie erstellt man eine zufällige Folge von Zahlen und Buchstaben mit einer Länge von 5?
22 Stimmen
Ein Beitrag mit so vielen positiven Bewertungen und so vielen guten Antworten hat es nicht verdient, als geschlossen markiert zu werden. Ich stimme dafür, dass es wieder geöffnet wird.
0 Stimmen
Als Kryptograph sehe ich hauptsächlich absolut falsche Antworten mit vielen positiven Bewertungen, um ehrlich zu sein. Wenn Sie eine sichere Zufallszeichenkette haben wollen, dann beschäftigen Sie sich bitte selbst mit Zufälligkeit und unvoreingenommenen Verteilungen, denn die stehen nicht auf der ersten Seite der Antworten.
0 Stimmen
Übrigens, hier ist eine Sammlung von Erzeugung von Zufallszeichenfolgen in verschiedenen Programmiersprachen .
0 Stimmen
Wer hat etwas von Sicherheit gesagt? Der OP fragte nur, wie man eine Zeichenkette erzeugt.