Generar cadena alfanumérica aleatoria en Swift


208

¿Cómo puedo generar una cadena alfanumérica aleatoria en Swift?

Respuestas:


355

Actualización Swift 4.2

Swift 4.2 introdujo mejoras importantes al tratar con valores y elementos aleatorios. Puede leer más sobre esas mejoras aquí . Aquí está el método reducido a unas pocas líneas:

func randomString(length: Int) -> String {
  let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  return String((0..<length).map{ _ in letters.randomElement()! })
}

Actualización de 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
}

Respuesta original:

func randomStringWithLength (len : Int) -> NSString {

    let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

    var randomString : NSMutableString = NSMutableString(capacity: len)

    for (var i=0; i < len; i++){
        var length = UInt32 (letters.length)
        var rand = arc4random_uniform(length)
        randomString.appendFormat("%C", letters.characterAtIndex(Int(rand)))
    }

    return randomString
}

1
¿Hay alguna manera de modificar lo anterior para asegurar que la cadena alfanumérica generada tenga solo 6 u 8 caracteres?
ksa_coder

44
randomString (longitud: 6) o randomString (longitud: 8)
Simon H

58

Aquí hay una solución lista para usar en sintaxis Swiftier . Simplemente puede copiarlo y pegarlo:

func randomAlphaNumericString(length: Int) -> String {
    let allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    let allowedCharsCount = UInt32(allowedChars.characters.count)
    var randomString = ""

    for _ in 0..<length {
        let randomNum = Int(arc4random_uniform(allowedCharsCount))
        let randomIndex = allowedChars.index(allowedChars.startIndex, offsetBy: randomNum)
        let newCharacter = allowedChars[randomIndex]
        randomString += String(newCharacter)
    }

    return randomString
}

Si prefiere un Framework que también tenga algunas características más prácticas, no dude en consultar mi proyecto HandySwift . También incluye una hermosa solución para cadenas alfanuméricas aleatorias :

String(randomWithLength: 8, allowedCharactersType: .alphaNumeric) // => "2TgM5sUG"

49

También puede usarlo de la siguiente manera:

extension String {

    static func random(length: Int = 20) -> String {

        let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        var randomString: String = ""

        for _ in 0..<length {

            let randomValue = arc4random_uniform(UInt32(base.characters.count))
            randomString += "\(base[base.startIndex.advancedBy(Int(randomValue))])"
        }

        return randomString
    }
}

Uso simple:

let randomString = String.random()

Sintaxis de Swift 3:

extension String {

    static func random(length: Int = 20) -> String {
        let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        var randomString: String = ""

        for _ in 0..<length {
            let randomValue = arc4random_uniform(UInt32(base.characters.count))
            randomString += "\(base[base.index(base.startIndex, offsetBy: Int(randomValue))])"
        }
        return randomString
    }
}

Sintaxis de Swift 4:

extension String {

    static func random(length: Int = 20) -> String {
        let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        var randomString: String = ""

        for _ in 0..<length {
            let randomValue = arc4random_uniform(UInt32(base.count))
            randomString += "\(base[base.index(base.startIndex, offsetBy: Int(randomValue))])"
        }
        return randomString
    }
}


11

Con Swift 4.2, su mejor opción es crear una cadena con los personajes que desee y luego usar randomElement para elegir cada personaje:

let length = 32
let characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let randomCharacters = (0..<length).map{_ in characters.randomElement()!}
let randomString = String(randomCharacters)

Detallo más sobre estos cambios aquí .


3
En lugar de mapa, puede usar compactMap y luego no hay necesidad de hacerlo. operador. ;)
Kristaps Grinbergs

1
¡Hola @KristapsGrinbergs! Pensé que el desenvolvimiento forzado tendría un mejor rendimiento que usar compactMap.
leogdion

11

ACTUALIZADO 2019.

En el caso inusual que

el rendimiento importa.

Aquí hay una función extremadamente clara que almacena en caché :

func randomNameString(length: Int = 7)->String{
    
    enum s {
        static let c = Array("abcdefghjklmnpqrstuvwxyz12345789")
        static let k = UInt32(c.count)
    }
    
    var result = [Character](repeating: "-", count: length)
    
    for i in 0..<length {
        let r = Int(arc4random_uniform(s.k))
        result[i] = s.c[r]
    }
    
    return String(result)
}

Esto es para cuando tiene un juego de caracteres fijo y conocido .

Consejo práctico:

Tenga en cuenta que "abcdefghjklmnpqrstuvwxyz12345789" evita los caracteres 'malos'

No hay 0, o, O, i, etc ... los caracteres que los humanos a menudo confunden.

Esto se hace a menudo para códigos de reserva y códigos similares que utilizarán los clientes humanos.


1
Votación a favor repeating:count:.
Coeur

10

Simple y rápido : UUID (). UuidString

// Devuelve una cadena creada a partir del UUID, como "E621E1F8-C36C-495A-93FC-0C247A3E6E5F"

public var uuidString: String {get}

https://developer.apple.com/documentation/foundation/uuid

Swift 3.0

let randomString = UUID().uuidString //0548CD07-7E2B-412B-AD69-5B2364644433
print(randomString.replacingOccurrences(of: "-", with: ""))
//0548CD077E2B412BAD695B2364644433

EDITAR

Por favor no confunda con UIDevice.current.identifierForVendor?.uuidStringque no dará valores aleatorios.


6

Versión Swift 2.2

// based on https://gist.github.com/samuel-mellert/20b3c99dec168255a046
// which is based on https://gist.github.com/szhernovoy/276e69eb90a0de84dd90
// Updated to work on Swift 2.2

func randomString(length: Int) -> String {
    let charactersString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    let charactersArray : [Character] = Array(charactersString.characters)

    var string = ""
    for _ in 0..<length {
        string.append(charactersArray[Int(arc4random()) % charactersArray.count])
    }

    return string
}

Básicamente llame a este método que generará una cadena aleatoria de la longitud del entero entregado a la función. Para cambiar los posibles caracteres solo edite la cadena caracteresString. Soporta caracteres unicode también.

https://gist.github.com/gingofthesouth/54bea667b28a815b2fe33a4da986e327


2
Por alguna desafortunada razón, esta versión ocasionalmente da unEXC_BAD_INSTRUCTION
Joe

Hola Joe, ¿tienes algún código de demostración que pueda reproducir este error?
Ernest Cunningham

Déjame ver qué puedo hacer; Solo lo estaba llamando como está en una salida de acción del IB con let random = randomString(16). El EXC solo estaba en un dispositivo real y no lo vi en un simulador y era intermitente en el dispositivo.
Joe

1
Vea esta pregunta SO por la razón por la cual esto falla la mitad del tiempo en dispositivos de 32 bits: stackoverflow.com/questions/25274265/…
julien_c

Importante: random % count no no (siempre) crear una distribución uniforme. Si esto es relevante para usted, busque otras respuestas que utilicen arc4random_uniform().
Raphael

6

Para las personas que no quieren escribir todo el conjunto de caracteres:

func randomAlphanumericString(length: Int) -> String  {
    enum Statics {
        static let scalars = [UnicodeScalar("a").value...UnicodeScalar("z").value,
                              UnicodeScalar("A").value...UnicodeScalar("Z").value,
                              UnicodeScalar("0").value...UnicodeScalar("9").value].joined()

        static let characters = scalars.map { Character(UnicodeScalar($0)!) }
    }

    let result = (0..<length).map { _ in Statics.characters.randomElement()! }
    return String(result)
}

5

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
}

1
¿Intentaste ese código? ¿Notó que la longitud de la cadena devuelta es incorrecta y que solo hay dígitos? Eche un vistazo a stackoverflow.com/q/39566062/1187415 , que tiene el mismo problema.
Martin R

@ MartininR gracias por señalar eso. He actualizado mi respuesta
S1LENT WARRIOR

5

Un Swift puro al azar Stringde cualquiera CharacterSet.

Uso: CharacterSet.alphanumerics.randomString(length: 100)

extension CharacterSet {
    /// extracting characters
    /// https://stackoverflow.com/a/52133647/1033581
    public func characters() -> [Character] {
        return codePoints().compactMap { UnicodeScalar($0) }.map { Character($0) }
    }
    public func codePoints() -> [Int] {
        var result: [Int] = []
        var plane = 0
        for (i, w) in bitmapRepresentation.enumerated() {
            let k = i % 8193
            if k == 8192 {
                plane = Int(w) << 13
                continue
            }
            let base = (plane + k) << 3
            for j in 0 ..< 8 where w & 1 << j != 0 {
                result.append(base + j)
            }
        }
        return result
    }

    /// building random string of desired length
    /// https://stackoverflow.com/a/42895178/1033581
    public func randomString(length: Int) -> String {
        let charArray = characters()
        let charArrayCount = UInt32(charArray.count)
        var randomString = ""
        for _ in 0 ..< length {
            randomString += String(charArray[Int(arc4random_uniform(charArrayCount))])
        }
        return randomString
    }
}

La characters()función es mi implementación más rápida conocida .


3
func randomString(length: Int) -> String {
    // whatever letters you want to possibly appear in the output (unicode handled properly by Swift)
    let letters = "abcABC012你好吗😀🐱💥∆𝚹∌⌘"
    let n = UInt32(letters.characters.count)
    var out = ""
    for _ in 0..<length {
        let index = letters.startIndex.advancedBy(Int(arc4random_uniform(n)))
        out.append(letters[index])
    }
    return out
}

3

Mi implementación aún más rápida de la pregunta:

func randomAlphanumericString(length: Int) -> String {

    let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".characters
    let lettersLength = UInt32(letters.count)

    let randomCharacters = (0..<length).map { i -> String in
        let offset = Int(arc4random_uniform(lettersLength))
        let c = letters[letters.startIndex.advancedBy(offset)]
        return String(c)
    }

    return randomCharacters.joinWithSeparator("")
}

3

Sin bucle, aunque está limitado a 43 caracteres. Si necesita más, se puede modificar. Este enfoque tiene dos ventajas sobre el uso exclusivo de un UUID:

  1. Mayor entropía mediante el uso de letras minúsculas, ya que UUID()solo genera letras mayúsculas
  2. A UUIDtiene como máximo 36 caracteres de largo (incluidos los 4 guiones), pero solo 32 caracteres sin él. Si necesita algo más, o no quiere incluir guiones, el uso de los base64EncodedStringmanejadores esto

Además, esta función utiliza a UIntpara evitar números negativos.

 func generateRandom(size: UInt) -> String {
        let prefixSize = Int(min(size, 43))
        let uuidString = UUID().uuidString.replacingOccurrences(of: "-", with: "")
        return String(Data(uuidString.utf8)
            .base64EncodedString()
            .replacingOccurrences(of: "=", with: "")
            .prefix(prefixSize))
    }

Llamarlo en un bucle para verificar la salida:

for _ in 0...10 {
    print(generateRandom(size: 32))
}

Que produce:

Nzk3NjgzMTdBQ0FBNDFCNzk2MDRENzZF
MUI5RURDQzE1RTdCNDA3RDg2MTI4QkQx
M0I3MjJBRjVFRTYyNDFCNkI5OUM1RUVC
RDA1RDZGQ0IzQjI1NDdGREI3NDgxM0Mx
NjcyNUQyOThCNzhCNEVFQTk1RTQ3NTIy
MDkwRTQ0RjFENUFGNEFDOTgyQTUxODI0
RDU2OTNBOUJGMDE4NDhEODlCNEQ1NjZG
RjM2MTUxRjM4RkY3NDU2OUFDOTI0Nzkz
QzUwOTE1N0U1RDVENDE4OEE5NTM2Rjcy
Nzk4QkMxNUJEMjYwNDJDQjhBQkY5QkY5
ODhFNjU0MDVEMUI2NEI5QUIyNjNCNkVF

3

Swift 5.0

// Generating Random String
func randomString(length: Int) -> String {
    let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    return String((0..<length).map{ _ in letters.randomElement()! })
}
// Calling to string
label.text = randomString(length: 3)

2

El problema con las respuestas a las preguntas "Necesito cadenas aleatorias" (en cualquier idioma) es que prácticamente todas las soluciones utilizan una especificación primaria defectuosa de longitud de cadena . Las preguntas en sí rara vez revelan por qué se necesitan las cadenas aleatorias, pero desafiaría que rara vez necesite cadenas aleatorias de longitud, digamos 8. Lo que invariablemente necesita es un cierto número de cadenas únicas , por ejemplo, para usar como identificadores para algún propósito.

Hay dos formas principales de obtener cadenas estrictamente únicas : determinista (que no es aleatorio) y almacenar / comparar (que es oneroso). qué hacemos? Renunciamos al fantasma. Vamos con unicidad probabilística en su lugar. Es decir, aceptamos que existe un riesgo (aunque pequeño) de que nuestras cadenas no sean únicas. Aquí es donde es útil comprender la probabilidad de colisión y la entropía .

Por lo tanto, reformularé la necesidad invariable de necesitar cierto número de cadenas con un pequeño riesgo de repetición. Como ejemplo concreto, supongamos que desea generar un potencial de 5 millones de ID. No desea almacenar y comparar cada nueva cadena, y desea que sean aleatorias, por lo que acepta cierto riesgo de repetición. Como ejemplo, digamos un riesgo de menos de 1 en un billón de posibilidades de repetición. Entonces, ¿qué longitud de cuerda necesitas? Bueno, esa pregunta no está especificada, ya que depende de los caracteres utilizados. Pero lo más importante, es equivocado. Lo que necesita es una especificación de la entropía de las cadenas, no su longitud. La entropía puede estar directamente relacionada con la probabilidad de una repetición en cierto número de cadenas. La longitud de la cuerda no puede.

Y aquí es donde una biblioteca como EntropyString puede ayudar. Para generar ID aleatorios que tengan menos de 1 en un billón de posibilidades de repetirse en 5 millones de cadenas usando EntropyString:

import EntropyString

let random = Random()
let bits = Entropy.bits(for: 5.0e6, risk: 1.0e12)
random.string(bits: bits)

"Rrrj6pN4d6GBrFLH4"

EntropyStringutiliza un conjunto de caracteres con 32 caracteres de forma predeterminada. Existen otros conjuntos de caracteres predefinidos, y también puede especificar sus propios caracteres. Por ejemplo, generar ID con la misma entropía que la anterior pero utilizando caracteres hexadecimales:

import EntropyString

let random = Random(.charSet16)
let bits = Entropy.bits(for: 5.0e6, risk: 1.0e12)
random.string(bits: bits)

"135fe71aec7a80c02dce5"

Tenga en cuenta la diferencia en la longitud de la cadena debido a la diferencia en el número total de caracteres en el conjunto de caracteres utilizado. El riesgo de repetición en el número especificado de cadenas potenciales es el mismo. Las longitudes de cadena no son. Y lo mejor de todo, el riesgo de repetición y el número potencial de cadenas es explícito. No más adivinanzas con la longitud de la cuerda.


2

Si su cadena aleatoria debe ser segura-aleatoria, use esto:

import Foundation
import Security

// ...

private static func createAlphaNumericRandomString(length: Int) -> String? {
    // create random numbers from 0 to 63
    // use random numbers as index for accessing characters from the symbols string
    // this limit is chosen because it is close to the number of possible symbols A-Z, a-z, 0-9
    // so the error rate for invalid indices is low
    let randomNumberModulo: UInt8 = 64

    // indices greater than the length of the symbols string are invalid
    // invalid indices are skipped
    let symbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

    var alphaNumericRandomString = ""

    let maximumIndex = symbols.count - 1

    while alphaNumericRandomString.count != length {
        let bytesCount = 1
        var randomByte: UInt8 = 0

        guard errSecSuccess == SecRandomCopyBytes(kSecRandomDefault, bytesCount, &randomByte) else {
            return nil
        }

        let randomIndex = randomByte % randomNumberModulo

        // check if index exceeds symbols string length, then skip
        guard randomIndex <= maximumIndex else { continue }

        let symbolIndex = symbols.index(symbols.startIndex, offsetBy: Int(randomIndex))
        alphaNumericRandomString.append(symbols[symbolIndex])
    }

    return alphaNumericRandomString
}

1

Si solo necesita un identificador único, UUID().uuidStringpuede servir a sus propósitos.


1

Actualizado para Swift 4. Use una variable almacenada perezosa en la extensión de clase. Esto solo se calcula una vez.

extension String {

    static var chars: [Character] = {
        return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".map({$0})
    }()

    static func random(length: Int) -> String {
        var partial: [Character] = []

        for _ in 0..<length {
            let rand = Int(arc4random_uniform(UInt32(chars.count)))
            partial.append(chars[rand])
        }

        return String(partial)
    }
}

String.random(length: 10) //STQp9JQxoq

1

SWIFT 4

Uso de RandomNumberGenerator para un mejor rendimiento como recomendación de Apple

Uso: String.random(20) Resultado:CifkNZ9wy9jBOT0KJtV4

extension String{
   static func random(length:Int)->String{
        let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        var randomString = ""

        while randomString.utf8.count < length{
            let randomLetter = letters.randomElement()
            randomString += randomLetter?.description ?? ""
        }
        return randomString
    }
}

0

Esta es la solución más rápida que se me ocurrió. Swift 3.0

extension String {
    static func random(length: Int) -> String {
        let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        let randomLength = UInt32(letters.characters.count)

        let randomString: String = (0 ..< length).reduce(String()) { accum, _ in
            let randomOffset = arc4random_uniform(randomLength)
            let randomIndex = letters.index(letters.startIndex, offsetBy: Int(randomOffset))
            return accum.appending(String(letters[randomIndex]))
        }

        return randomString
    } 
}

-1
func randomUIDString(_ wlength: Int) -> String {

    let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    var randomString = ""

    for _ in 0 ..< wlength {
        let length = UInt32 (letters.length)
        let rand = arc4random_uniform(length)
        randomString = randomString.appendingFormat("%C", letters.character(at: Int(rand)));
    }

    return randomString
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.