Générer une chaîne alphanumérique aléatoire dans Cocoa

145

Je veux appeler une méthode, lui transmettre la longueur et la faire générer une chaîne alphanumérique aléatoire.

Existe-t-il des bibliothèques d'utilitaires qui peuvent avoir un tas de ces types de fonctions?

quartier
la source

Réponses:

312

Voici une implémentation rapide et sale. N'a pas été testé.

NSString *letters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

-(NSString *) randomStringWithLength: (int) len {

    NSMutableString *randomString = [NSMutableString stringWithCapacity: len];

    for (int i=0; i<len; i++) {
         [randomString appendFormat: @"%C", [letters characterAtIndex: arc4random_uniform([letters length])]];
    }

    return randomString;
}
Jeff B
la source
1
genRandStringLength doit simplement renvoyer randomString. Il n'y a aucune raison d'allouer et d'initier (et pas de relâcher automatiquement!) Une toute nouvelle chaîne.
kevingessner
5
Je vais juste mettre ça là-bas. Il n'y a aucune raison d'utiliser un NSStringpour lettersquand un simple chartableau fonctionnerait très bien. En fait, l'utilisation [letters characterAtIndex:(rand() % [letters length])]me semble moins concise que juste letters[rand() % strlen(letters)]. Les classes Foundation sont vraiment utiles, mais pour les choses les plus simples, elles peuvent servir à obfustifier notre code plutôt qu'à l'améliorer.
Jonathan Sterling
3
vous voudrez peut-être à la %Cplace de %c, car characterAtIndex:renvoie ununichar
user102008
8
L'utilisation d'arc4random générerait un résultat biaisé lorsque la longueur du tableau n'est pas une puissance de deux. arc4random_uniform corrige ce problème.
jlukanta
9
oh le compilateur donnerait un avertissement pour perte de précision, donc mieux vaut lancer en intarc4random_uniform((int)[letters length])
knshn
103

Pas exactement ce que vous demandez, mais toujours utile:

[[NSProcessInfo processInfo] globallyUniqueString]

Exemple de sortie:

450FEA63-2286-4B49-8ACC-9822C7D4356B-1376-00000239A4AC4FD5
mes yeux sont aveugles
la source
2
C'est de loin le moyen le plus court et le plus simple de répondre à la question.
adib
Même s'il contient des traits d'union - si ce n'est pas un problème, c'est génial!
fatuhoku
Meilleure réponse d'un mile
Rambatino
Parfait pour mon besoin de "générer une chaîne alphanumérique aléatoire dans le cacao". Pas exactement ce que demande OP uniquement parce qu'il ajoute l'exigence de "passer la longueur" que YAGNI!
jkoreska
5
C'est probablement correct pour la plupart des utilisations, mais NE PAS UTILISER si vous avez besoin d'une chaîne aléatoire pour des raisons de sécurité. Unique! = Aléatoire. La longueur est constante, la plage de caractères utilisée est limitée (0-9, AF, - = 17, vs 62 pour aZ. 0-9). Cette chaîne est unique mais prévisible.
amcc
67
NSString *alphabet  = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY0123456789";
NSMutableString *s = [NSMutableString stringWithCapacity:20];
for (NSUInteger i = 0U; i < 20; i++) {
    u_int32_t r = arc4random() % [alphabet length];
    unichar c = [alphabet characterAtIndex:r];
    [s appendFormat:@"%C", c];
}
Melvin
la source
13
Avez-vous vraiment besoin d'interroger la durée de alphabetchaque fois? Il est constant et ne dépend pas de la boucle.
jv42
1
Testé en générant 1M de mots de passe de 10 caractères chacun et cela fonctionne très bien.
NaXir
1
NSArraycache son length, ne devrait pas être un goulot d'étranglement de performance.
Pascal
Je suis d'accord. C'est un simple accès à la propriété. Cela ne compte pas chaque fois que vous demandez.
devios1
45

Vous pouvez sûrement raccourcir:

+(NSString*)generateRandomString:(int)num {
    NSMutableString* string = [NSMutableString stringWithCapacity:num];
    for (int i = 0; i < num; i++) {
        [string appendFormat:@"%C", (unichar)('a' + arc4random_uniform(26))];
    }
    return string;
}
John Riselvato
la source
2
pourquoi le -1? Il n'y a rien de mal à cela. C'est comme la plus petite des réponses les plus optimisées.
John Riselvato
5
Rien de mal avec celui-ci. C'est une solution agréable et concise. (Je souhaite que SO applique un commentaire sur les
votes négatifs
Meilleure solution. Le seul commentaire, j'en ferais une méthode statique.
Andrei Tchijov
9
Le seul inconvénient est que ce n'est pas vraiment alphanumérique, mais juste des lettres minuscules. ;-)
PrimaryFeather
Je pense que vous devez changer le 25 en 26, sinon vous n'obtiendrez jamais «z».
Marcus Adams
28

Si vous souhaitez vous limiter aux caractères hexadécimaux uniquement, l'option la plus simple consiste à générer un UUID:

NSString *uuid = [NSUUID UUID].UUIDString;

Exemple de sortie: 16E3DF0B-87B3-4162-A1A1-E03DB2F59654.

Si vous voulez une chaîne aléatoire plus petite, vous ne pouvez saisir que les 8 premiers caractères.

Il est une version 4 UUID qui signifie que le premier caractère du groupe 3 et 4 ne sont pas au hasard (ils seront toujours 4et l' un des 8, 9, Aou B).

Tous les autres caractères de la chaîne sont entièrement aléatoires et vous pouvez générer des millions d'UUID chaque seconde pendant des centaines d'années sans grand risque que le même UUID soit généré deux fois.

Abhi Beckert
la source
1
Vous pouvez simplement utiliserNSString *uuid = [UUID UUID]
orkoden
@orkoden merci, mon code provenait d'un code iOS 5. Je mettrai à jour ma réponse pour utiliser la nouvelle API iOS 6 / OS X 10.8.
Abhi Beckert
@AbhiBeckert est-il sûr de n'utiliser que les 8 premiers caractères sans risquer d'obtenir les mêmes 8 premiers caractères?
Vishal Singh
1
@VishalSingh oui, c'est sûr, même si évidemment plus vous le faites court, plus votre risque de collision est élevé.
Abhi Beckert
3
NSString * uuid = [UUID UUID] .UUIDString; vous donnera l'erreur "Utilisation de l'identifiant non déclaré UUID", donc juste un petit changement dans le code utilise NSString * uuid = [NSUUID UUID] .UUIDString;
Abbas Mulani
24

Une version de catégorie de la réponse de Jeff B.

NSString + Random.h

#import <Foundation/Foundation.h>

@interface NSString (Random)

+ (NSString *)randomAlphanumericStringWithLength:(NSInteger)length;

@end

NSString + Random.m

#import "NSString+Random.h"

 @implementation NSString (Random)

+ (NSString *)randomAlphanumericStringWithLength:(NSInteger)length
{ 
    NSString *letters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    NSMutableString *randomString = [NSMutableString stringWithCapacity:length];

    for (int i = 0; i < length; i++) {
        [randomString appendFormat:@"%C", [letters characterAtIndex:arc4random() % [letters length]]];
    }

    return randomString;
}

@end
keithyip
la source
1
Ceci est un excellent exemple de la façon d'utiliser les catégories!
ElmerCat du
7

Vous pouvez également simplement générer un UUID. Bien qu'ils ne soient pas vraiment aléatoires, ils sont complexes et uniques, ce qui les rend aléatoires pour la plupart des utilisations. Générez-en une sous forme de chaîne, puis prenez une plage de caractères égale à la longueur transmise.

TechZen
la source
Je déconseillerais fortement cela pour tout ce qui concerne la sécurité. Le pseudo-aléatoire est l'une des plus grandes vunerabilities que les pirates utilisent dans les systèmes de pénétration, car ils offrent une prévisibilité. Utilisez aussi près que possible du réel aléatoire.
Shayne
5

Rapide

func randomStringWithLength(length: Int) -> String {
    let alphabet = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    let upperBound = UInt32(count(alphabet))
    return String((0..<length).map { _ -> Character in
        return alphabet[advance(alphabet.startIndex, Int(arc4random_uniform(upperBound)))]
    })
}
ma11hew28
la source
1
Court et doux. Cela a fonctionné pour moi, sauf lorsque j'ai modifié l'alphabet, il s'est écrasé car la longueur de l'alphabet est codée en dur. J'ai remplacé le 64 par UInt32 (count (alphabet))
scootermg
D'ACCORD. J'ai remplacé le codé 64en dur par un fichier upperBound. Je calcule en upperBounddehors du bloc parce que je pense que cela fonctionne mieux.
ma11hew28
Impossible d'appeler 'count' avec une liste d'arguments de type '(String)' handles en utilisant 'alphabet.characters.count'
Zaporozhchenko Oleksandr
return alphabet [alphabet.startIndex.advancedBy (Int (arc4random_uniform (upperBound)))
Zaporozhchenko Oleksandr
4

Voici une manière différente de s'y attaquer. Au lieu d'utiliser une chaîne de caractères préparée, vous pouvez convertir des entiers et des caractères et générer une liste dynamique de caractères à sélectionner. C'est plutôt simple et rapide, mais avec un peu plus de code.

int charNumStart = (int) '0';
int charNumEnd = (int) '9';
int charCapitalStart = (int) 'A';
int charCapitalEnd = (int) 'Z';
int charLowerStart = (int) 'a';
int charLowerEnd = (int) 'z';

int amountOfChars = (charNumEnd - charNumStart) + (charCapitalEnd - charCapitalStart) + (charLowerEnd - charLowerStart); // amount of the characters we want.
int firstGap = charCapitalStart - charNumEnd; // there are gaps of random characters between numbers and uppercase letters, so this allows us to skip those.
int secondGap = charLowerStart - charCapitalEnd; // similar to above, but between uppercase and lowercase letters.

// START generates a log to show us which characters we are considering for our UID.
NSMutableString *chars = [NSMutableString stringWithCapacity:amountOfChars];
for (int i = charNumStart; i <= charLowerEnd; i++) {
    if ((i >= charNumStart && i <= charNumEnd) || (i >= charCapitalStart && i <= charCapitalEnd) || (i >= charLowerStart && i <= charLowerEnd)) {
        [chars appendFormat:@"\n%c", (char) i];
    }
}
NSLog(@"chars: %@", chars);
// END log

// Generate a uid of 20 characters that chooses from our desired range.
int uidLength = 20;
NSMutableString *uid = [NSMutableString stringWithCapacity:uidLength];
for (int i = 0; i < uidLength; i++) {
    // Generate a random number within our character range.
    int randomNum = arc4random() % amountOfChars;
    // Add the lowest value number to line this up with a desirable character.
    randomNum += charNumStart;
    // if the number is in the letter range, skip over the characters between the numbers and letters.
    if (randomNum > charNumEnd) {
        randomNum += firstGap;
    }
    // if the number is in the lowercase letter range, skip over the characters between the uppercase and lowercase letters.
    if (randomNum > charCapitalEnd) {
        randomNum += secondGap;
    }
    // append the chosen character.
    [uid appendFormat:@"%c", (char) randomNum];
}
NSLog(@"uid: %@", uid);

// Generate a UID that selects any kind of character, including a lot of punctuation. It's a bit easier to do it this way.
int amountOfAnyCharacters = charLowerEnd - charNumStart; // A new range of characters.
NSMutableString *multiCharUid = [NSMutableString stringWithCapacity:uidLength];
for (int i = 0; i < uidLength; i++) {
    // Generate a random number within our new character range.
    int randomNum = arc4random() % amountOfAnyCharacters;
    // Add the lowest value number to line this up with our range of characters.
    randomNum += charNumStart;
    // append the chosen character.
    [multiCharUid appendFormat:@"%c", (char) randomNum];
}
NSLog(@"multiCharUid: %@", multiCharUid);

Lorsque je fais de la génération aléatoire de caractères, je préfère travailler directement avec des entiers et les convertir, au lieu d'écrire la liste des caractères dont je veux tirer. La déclaration des variables en haut le rend plus indépendant du système, mais ce code suppose que les nombres auront une valeur inférieure à celle des lettres et que les lettres majuscules auront une valeur inférieure aux lettres minuscules.

rjferguson
la source
3

Solution alternative dans Swift

func generateString(len: Int) -> String {
    let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    let lettersLength = UInt32(countElements(letters))
    let result = (0..<len).map { _ -> String in
        let idx = Int(arc4random_uniform(lettersLength))
        return String(letters[advance(letters.startIndex, idx)])
    }
    return "".join(result)
}
Gralex
la source
2

En plus de la bonne réponse donnée par Melvin, voici une fonction que j'ai créée ( dans SWIFT! ) Pour obtenir une chaîne aléatoire:

func randomStringOfLength(length:Int)->String{
    var wantedCharacters:NSString="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY0123456789"
    var s=NSMutableString(capacity: length)
    for (var i:Int = 0; i < length; i++) {
        let r:UInt32 = arc4random() % UInt32( wantedCharacters.length)
        let c:UniChar = wantedCharacters.characterAtIndex( Int(r) )
        s.appendFormat("%C", c)
    }
    return s
}

Voici un résultat de test de l'appel randomStringOfLength(10): uXa0igA8wm

Stan Tatarnykov
la source
2

Génère une chaîne aléatoire alphanumérique minuscule avec une longueur donnée:

-(NSString*)randomStringWithLength:(NSUInteger)length
{
    NSMutableString* random = [NSMutableString stringWithCapacity:length];

    for (NSUInteger i=0; i<length; i++)
    {
        char c = '0' + (unichar)arc4random()%36;
        if(c > '9') c += ('a'-'9'-1);
        [random appendFormat:@"%c", c];
    }

    return random;
}
Erkanyildiz
la source
2

Modification de quelques idées ici, et dans done Swift 4.0

extension String
{
    subscript (i: Int) -> Character
    {
        return self[index(startIndex, offsetBy:i)]
    }

    static func Random(length:Int=32, alphabet:String="ABCDEF0123456789") -> String
    {
        let upperBound = UInt32(alphabet.count)
        return String((0..<length).map { _ -> Character in
            return alphabet[Int(arc4random_uniform(upperBound))]
        })
    }
}

Usage:

let myHexString = String.Random()
let myLongHexString = String.Random(length:64)
let myLettersString = String.Random(length:32, alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZ")
Wex
la source
1

Si vous voulez une chaîne Unicode aléatoire, vous pouvez créer des octets aléatoires, puis utiliser les octets valides.

    OSStatus sanityCheck = noErr;
    uint8_t * randomBytes = NULL;
    size_t length = 200; // can of course be variable

    randomBytes = malloc( length * sizeof(uint8_t) );
    memset((void *)randomBytes, 0x0, length);

    sanityCheck = SecRandomCopyBytes(kSecRandomDefault, length, randomBytes);

    if (sanityCheck != noErr) NSLog(@"Error generating random bytes, OSStatus == %ld.", sanityCheck);

    NSData* randomData = [[NSData alloc] initWithBytes:(const void *)randomBytes length: length];
    if (randomBytes) free(randomBytes);

    NSString* dataString = [[NSString alloc] initWithCharacters:[randomData bytes] length:[randomData length]];  // create an NSString from the random bytes
    NSData* tempData = [dataString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];             // remove illegal characters from string
    NSString* randomString = [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding];

La conversion de NSString en NSData et inversement est nécessaire pour obtenir une chaîne UTF-8 valide. Sachez que la longueur ne sera pas nécessairement la longueur de la NSString créée à la fin.

Orkoden
la source
1

Je l'ai fait en utilisant un simple char[]au lieu d'un NSString *pour l'alphabet. J'ai ajouté ceci à une catégorie NSString.

static const char __alphabet[] =
    "0123456789"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz";
+ (NSString *)randomString:(int)length
{
    NSMutableString *randomString = [NSMutableString stringWithCapacity:length];
    u_int32_t alphabetLength = (u_int32_t)strlen(__alphabet);
    for (int i = 0; i < length; i++) {
        [randomString appendFormat:@"%c", __alphabet[arc4random_uniform(alphabetLength)]];
    }
    return randomString;
}
M. T
la source
1
static NSUInteger length = 32;
static NSString *letters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
NSMutableString * randomString = [NSMutableString stringWithCapacity:length];
for (NSInteger i = 0; i < length; ++i) {
    [randomString appendFormat: @"%C", [letters characterAtIndex:(NSUInteger)arc4random_uniform((u_int32_t)[letters length])]];
}
Peter Lapisu
la source
1

Méthode pour appeler:


NSString *string = [self stringWithRandomSuffixForFile:@"file.pdf" withLength:4]

Méthode:


- (NSString *)stringWithRandomSuffixForFile:(NSString *)file withLength:(int)length
{
    NSString *alphabet = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    NSString *fileExtension = [file pathExtension];
    NSString *fileName = [file stringByDeletingPathExtension];
    NSMutableString *randomString = [NSMutableString stringWithFormat:@"%@_", fileName];

    for (int x = 0; x < length; x++) {
        [randomString appendFormat:@"%C", [alphabet characterAtIndex: arc4random_uniform((int)[alphabet length]) % [alphabet length]]];
    }
    [randomString appendFormat:@".%@", fileExtension];

    NSLog(@"## randomString: %@ ##", randomString);
    return randomString;
}

Résultats:


## randomString: file_Msci.pdf ##
## randomString: file_xshG.pdf ##
## randomString: file_abAD.pdf ##
## randomString: file_HVwV.pdf ##
émotalité
la source
1

pour Swift 3.0

func randomString(_ length: Int) -> String {

    let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    let len = UInt32(letters.length)

    var randomString = ""

    for _ in 0 ..< length {
        let rand = arc4random_uniform(len)
        var nextChar = letters.character(at: Int(rand))
        randomString += NSString(characters: &nextChar, length: 1) as String
    }

    return randomString
}
GUERRIER S1LENT
la source
0
#define ASCII_START_NUMERS 0x30
#define ASCII_END_NUMERS 0x39
#define ASCII_START_LETTERS_A 0x41
#define ASCII_END_LETTERS_Z 0x5A
#define ASCII_START_LETTERS_a 0x61
#define ASCII_END_LETTERS_z 0x5A

-(NSString *)getRandomString:(int)length {
    NSMutableString *result = [[NSMutableString alloc]init];
    while (result.length != length) {
        NSMutableData* data = [NSMutableData dataWithLength:1];
        SecRandomCopyBytes(kSecRandomDefault, 1, [data mutableBytes]);
        Byte currentChar = 0;
        [data getBytes:&currentChar length:1];
        NSString *s = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        if (currentChar > ASCII_START_NUMERS && currentChar < ASCII_END_NUMERS) { // 0 to 0
            [result appendString:s];
            continue;
        }
        if (currentChar > ASCII_START_LETTERS_A && currentChar < ASCII_END_LETTERS_Z) { // 0 to 0
            [result appendString:s];
            continue;
        }
        if (currentChar > ASCII_START_LETTERS_a && currentChar < ASCII_END_LETTERS_z) { // 0 to 0
            [result appendString:s];
            continue;
        }
    }
    return result;
}
TtheTank
la source
0

Modification de la réponse de keithyip:

+ (NSString *)randomAlphanumericStringWithLength:(NSInteger)length
{
    static NSString * const letters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        srand(time(NULL));
    });

    NSMutableString *randomString = [NSMutableString stringWithCapacity:length];

    for (int i = 0; i < length; i++) {
        [randomString appendFormat:@"%C", [letters characterAtIndex:arc4random() % [letters length]]];
    }

    return randomString;
}
iutinvg
la source