Generar una cadena alfanumérica aleatoria en Cocoa

145

Quiero llamar a un método, pasarle la longitud y hacer que genere una cadena alfanumérica aleatoria.

¿Hay alguna biblioteca de utilidad que pueda tener un montón de este tipo de funciones?

Sala
fuente

Respuestas:

312

Aquí hay una implementación rápida y sucia. No ha sido probado.

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
fuente
1
genRandStringLength debería devolver randomString. No hay razón para asignar e iniciar (y no liberar automáticamente) una cadena completamente nueva.
kevingessner
55
Solo voy a poner esto por ahí. No hay razón para usar un NSStringpara letterscuando una charmatriz simple funcionaría bien. De hecho, el uso [letters characterAtIndex:(rand() % [letters length])]me parece menos conciso que solo letters[rand() % strlen(letters)]. Las clases de Foundation son realmente útiles, pero para las cosas más simples, pueden servir para ofuscar nuestro código en lugar de mejorarlo.
Jonathan Sterling
3
es posible que desee en %Clugar de %c, porque characterAtIndex:devuelve ununichar
user102008
8
El uso de arc4random generaría un resultado sesgado cuando la longitud de la matriz no es una potencia de dos. arc4random_uniform corrige esto.
jlukanta
9
oh, el compilador daría una advertencia por perder precisión, así que es mejor arc4random_uniform((int)[letters length])
enviarlo
103

No es exactamente lo que pides, pero sigue siendo útil:

[[NSProcessInfo processInfo] globallyUniqueString]

Salida de muestra:

450FEA63-2286-4B49-8ACC-9822C7D4356B-1376-00000239A4AC4FD5
myeyesareblind
fuente
2
Esta es, con mucho, la forma más corta y directa de abordar la cuestión.
adib
Incluso si tiene guiones, si eso no es una preocupación, ¡genial!
fatuhoku
La mejor respuesta por una milla
Rambatino
Perfecto para mi necesidad de "generar una cadena alfanumérica aleatoria en cacao". ¡No es exactamente lo que pregunta OP solo porque agrega el requisito de "pasar la longitud" que YAGNI!
jkoreska
55
Esto probablemente esté bien para la mayoría de los usos, pero NO LO USE si necesita una cadena aleatoria por motivos de seguridad. Único! = Aleatorio. La longitud es constante, el rango de caracteres utilizado es limitado (0-9, AF, - = 17, vs 62 para aZ. 0-9). Esta cadena es única pero predecible.
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
fuente
13
¿Realmente necesitas consultar la duración de alphabetcada vez? Es constante y no depende del bucle.
jv42
1
Probado mediante la generación de contraseñas de 1M de 10 caracteres cada una y funciona muy bien.
NaXir
1
NSArrayalmacena en caché su length, no debería ser un cuello de botella de rendimiento.
Pascal
Estoy de acuerdo. Es un simple acceso a la propiedad. No se cuenta cada vez que preguntas.
devios1
45

Seguramente puedes hacer esto más corto:

+(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
fuente
2
¿Por qué el -1? Esto no tiene nada de malo. Es como la más pequeña y optimizada de todas las respuestas.
John Riselvato
55
No hay nada malo con este. Es una solución agradable y concisa. (Me gustaría SO impondría un comentario en downvotes.)
Alvi
Mejor solución. El único comentario, lo haría método estático.
Andrei Tchijov
9
El único inconveniente es que no es realmente alfanumérico, sino solo letras minúsculas. ;-)
PrimaryFeather
Creo que necesita cambiar el 25 a 26, de lo contrario nunca obtendrá 'z'.
Marcus Adams
28

Si está dispuesto a limitarse solo a caracteres hexadecimales, la opción más simple es generar un UUID:

NSString *uuid = [NSUUID UUID].UUIDString;

Ejemplo de salida: 16E3DF0B-87B3-4162-A1A1-E03DB2F59654.

Si desea una cadena aleatoria más pequeña, puede tomar solo los primeros 8 caracteres.

Es un UUID la versión 4 que significa que el primer carácter de la 3ª y 4ª grupo no es al azar (que siempre será 4y uno de 8, 9, AoB ).

Todos los demás caracteres de la cadena son completamente aleatorios y puede generar millones de UUID por segundo durante cientos de años sin mucho riesgo de que se genere el mismo UUID dos veces.

Abhi Beckert
fuente
1
Simplemente puede usarNSString *uuid = [UUID UUID]
orkoden
@orkoden gracias, mi código era de algún código iOS 5. Actualizaré mi respuesta para usar la nueva API de iOS 6 / OS X 10.8.
Abhi Beckert
@AbhiBeckert ¿es seguro usar solo los primeros 8 caracteres sin el riesgo de obtener los mismos primeros 8 caracteres?
Vishal Singh
1
@VishalSingh sí, es seguro, aunque obviamente cuanto más corto sea, mayor es el riesgo de colisiones.
Abhi Beckert
3
NSString * uuid = [UUID UUID] .UUIDString; le dará el error "Uso de identificador no declarado UUID", por lo que solo un pequeño cambio en el código utilizará NSString * uuid = [NSUUID UUID] .UUIDString;
Abbas Mulani
24

Una versión de categoría de la respuesta 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
fuente
1
¡Este es un gran ejemplo de cómo usar Categorías!
ElmerCat
7

También podría simplemente generar un UUID. Si bien no son realmente aleatorios, son complejos y únicos, lo que los hace parecer aleatorios para la mayoría de los usos. Genere uno como una cadena y luego tome un rango de caracteres igual a la longitud pasada.

TechZen
fuente
Aconsejaría fuertemente contra esto para cualquier cosa relacionada con la seguridad. La seudoaleatoriedad es una de las mayores vulnerabilidades que los hackers usan en los sistemas penetrantes, ya que proporcionan previsibilidad. Use lo más cerca posible al azar real.
Shayne
5

Rápido

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
fuente
1
Corto y dulce. Funcionó para mí, excepto cuando alteré el alfabeto, se bloqueó porque la longitud del alfabeto está codificada.
Reemplacé
OKAY. Reemplacé el hardcoded 64con uno calculado upperBound. Calculo upperBoundfuera del bloque porque creo que funciona mejor.
ma11hew28
No se puede invocar 'count' con una lista de argumentos de tipo '(String)' maneja usando 'alphabet.characters.count'
Zaporozhchenko Oleksandr
return alphabet [alphabet.startIndex.advancedBy (Int (arc4random_uniform (upperBound)))
Zaporozhchenko Oleksandr
4

Aquí hay una forma diferente de abordarlo. En lugar de utilizar una cadena de caracteres preparada, puede emitir entre enteros y caracteres, y generar una lista dinámica de caracteres para seleccionar. Es bastante delgado y rápido, pero tiene un poco más de código.

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

Cuando estoy haciendo una generación aleatoria de personajes, prefiero trabajar directamente con enteros y lanzarlos, en lugar de escribir la lista de caracteres de los que quiero dibujar. Declarar las variables en la parte superior lo hace más independiente del sistema, pero este código supone que los números tendrán un valor más bajo que las letras, y que las letras mayúsculas tendrán un valor más bajo que las letras minúsculas.

rjferguson
fuente
3

Solución alternativa en 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
fuente
2

Agregando a la buena respuesta dada por Melvin, aquí hay una función que hice (¡ en SWIFT! ) Para obtener una cadena aleatoria:

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
}

Aquí hay un resultado de prueba de llamar randomStringOfLength(10): uXa0igA8wm

Stan Tatarnykov
fuente
2

Genera una cadena aleatoria alfanumérica en minúsculas con una longitud dada:

-(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
fuente
2

Modificación de algunas ideas aquí, y en Swift 4.0 hecho

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))]
        })
    }
}

Uso:

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

Si desea una cadena unicode aleatoria, puede crear bytes aleatorios y luego usar los válidos.

    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 conversión de NSString a NSData y viceversa es necesaria para obtener una cadena UTF-8 válida. Tenga en cuenta que la longitud no será necesariamente la longitud del NSString creado al final.

Orkoden
fuente
1

Hice esto usando un simple en char[]lugar de un NSString *para el alfabeto. Agregué esto a una categoría 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;
}
Señor t
fuente
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
fuente
1

Método para llamar:


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

Método:


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

Resultados:


## randomString: file_Msci.pdf ##
## randomString: file_xshG.pdf ##
## randomString: file_abAD.pdf ##
## randomString: file_HVwV.pdf ##
emotalidad
fuente
1

para 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
}
SOLDADO GUERRERO
fuente
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
fuente
0

Modificación de la respuesta 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
fuente