Genera stringa alfanumerica casuale in Swift


208

Come posso generare una stringa alfanumerica casuale in Swift?

Risposte:


355

Aggiornamento Swift 4.2

Swift 4.2 ha introdotto importanti miglioramenti nella gestione di valori ed elementi casuali. Puoi leggere di più su questi miglioramenti qui . Ecco il metodo ridotto a poche righe:

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

Aggiornamento 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
}

Risposta originale:

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
È possibile modificare quanto sopra per garantire che la stringa alfanumerica generata sia lunga solo 6 o 8 caratteri?
ksa_coder il

4
randomString (lunghezza: 6) o randomString (lunghezza: 8)
Simon H

58

Ecco una soluzione pronta per l'uso nella sintassi di Swiftier . Puoi semplicemente copiarlo e incollarlo:

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
}

Se preferisci un Framework che ha anche alcune funzioni più utili, non esitare a dare un'occhiata al mio progetto HandySwift . Include anche una bella soluzione per stringhe alfanumeriche casuali :

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

49

Puoi usarlo anche nel modo seguente:

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

Semplice utilizzo:

let randomString = String.random()

Sintassi 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
    }
}

Sintassi 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 la soluzione migliore è creare una stringa con i personaggi che desideri e quindi utilizzare randomElement per scegliere ogni personaggio:

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

Faccio maggiori dettagli su questi cambiamenti qui .


3
Invece di map puoi usare compactMap e non ce n'è bisogno! operatore. ;)
Kristaps Grinbergs,

1
Ehi @KristapsGrinbergs! Il mio pensiero era che il disimballaggio forzato avrebbe prestazioni migliori rispetto all'utilizzo di compactMap.
leogdion,

11

AGGIORNATO 2019.

Nel caso insolito quello

le prestazioni contano.

Ecco una funzione estremamente chiara che memorizza nella cache :

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

Questo è quando hai un set di caratteri noti e fissi.

Consiglio pratico:

Nota che "abcdefghjklmnpqrstuvwxyz12345789" evita i caratteri "cattivi"

Non c'è 0, o, O, i, ecc ... i personaggi che gli umani spesso confondono.

Questo viene spesso fatto per codici di prenotazione e codici simili che verranno utilizzati dai clienti umani.


1
Upoting per repeating:count:.
Cœur

10

Semplice e veloce - UUID (). UuidString

// Restituisce una stringa creata dall'UUID, ad esempio "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

MODIFICARE

Si prega di non confondere con UIDevice.current.identifierForVendor?.uuidStringesso non darà valori casuali.


6

Versione 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
}

Fondamentalmente chiama questo metodo che genererà una stringa casuale la lunghezza dell'intero passato alla funzione. Per cambiare i possibili caratteri basta modificare la stringa stringString. Supporta anche i caratteri Unicode.

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


2
Per qualche sfortunato motivo questa versione dà occasionalmente unEXC_BAD_INSTRUCTION
Joe

Ehi Joe, hai qualche codice demo in grado di riprodurre questo errore?
Ernest Cunningham,

Fammi vedere cosa posso fare; Lo stavo solo chiamando così com'è in una presa di azione IB con let random = randomString(16). L'EXC era solo su un dispositivo reale e non l'ho visto in un simulatore ed era intermittente sul dispositivo.
Joe,

1
Vedere questa domanda così per la ragione per cui questa si blocca la metà del tempo su dispositivi a 32 bit: stackoverflow.com/questions/25274265/...
julien_c

Importante: random % count non non (sempre) creare una distribuzione uniforme. Se questo è rilevante per te, guarda le altre risposte che usano arc4random_uniform().
Raffaello,

6

Per le persone che non vogliono scrivere l'intero set di caratteri:

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

per 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
Hai provato quel codice? Hai notato che la lunghezza della stringa restituita è errata e che ci sono solo cifre? Dai un'occhiata a stackoverflow.com/q/39566062/1187415 , che presenta lo stesso problema.
Martin R

@MartinR grazie per averlo sottolineato. ho aggiornato la mia risposta
S1LENT WARRIOR

5

Un puro Swift casuale Stringda qualsiasi 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()funzione è la mia implementazione più veloce conosciuta .


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

La mia implementazione ancora più rapida di Swift-ier della domanda:

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

Senza loop, anche se è limitato a 43 caratteri. Se hai bisogno di più, può essere modificato. Questo approccio presenta due vantaggi rispetto all'uso esclusivo di un UUID:

  1. Entropia maggiore usando lettere minuscole, in quanto UUID()genera solo lettere maiuscole
  2. A UUIDè lungo al massimo 36 caratteri (inclusi i 4 trattini), ma solo 32 caratteri senza. Se hai bisogno di qualcosa di più o non vuoi includere trattini, usa le base64EncodedStringmaniglie

Inoltre, questa funzione utilizza a UIntper evitare numeri negativi.

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

Chiamandolo in un ciclo per controllare l'output:

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

Che 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

Il problema con le risposte alle domande "Ho bisogno di stringhe casuali" (in qualsiasi lingua) è praticamente ogni soluzione utilizza una specifica primaria imperfetta della lunghezza della stringa . Le domande stesse raramente rivelano il motivo per cui sono necessarie stringhe casuali, ma sfiderei che raramente hai bisogno di stringhe casuali di lunghezza, diciamo 8. Ciò di cui hai sempre bisogno è un numero di stringhe uniche , ad esempio, da utilizzare come identificatori per qualche scopo.

Esistono due modi principali per ottenere stringhe rigorosamente uniche : deterministicamente (che non è casuale) e store / compare (che è oneroso). Cosa facciamo? Rinunciamo al fantasma. Andiamo invece con unicità probabilistica . Cioè, accettiamo che ci sia qualche (anche se piccolo) rischio che le nostre stringhe non siano uniche. Questo è dove capire la probabilità di collisione e l' entropia sono utili la .

Quindi riformulerò il bisogno invariabile in quanto necessitano di un certo numero di stringhe con un piccolo rischio di ripetizione. Ad esempio, supponiamo che tu voglia generare un potenziale di 5 milioni di ID. Non vuoi archiviare e confrontare ogni nuova stringa e vuoi che siano casuali, quindi accetti qualche rischio di ripetizione. Ad esempio, diciamo un rischio inferiore a 1 in una trilione di possibilità di ripetizione. Quindi di quale lunghezza hai bisogno? Bene, questa domanda è sottostimata in quanto dipende dai personaggi usati. Ma soprattutto, è fuorviato. Ciò di cui hai bisogno è una specifica dell'entropia delle stringhe, non della loro lunghezza. L'entropia può essere direttamente correlata alla probabilità di una ripetizione in un certo numero di stringhe. La lunghezza della stringa non può.

Ed è qui che può aiutare una libreria come EntropyString . Per generare ID casuali che hanno meno di 1 in trilioni di possibilità di ripetizione in 5 milioni di stringhe usando EntropyString:

import EntropyString

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

"Rrrj6pN4d6GBrFLH4"

EntropyStringutilizza un set di caratteri con 32 caratteri per impostazione predefinita. Esistono altri set di caratteri predefiniti e puoi anche specificare i tuoi personaggi. Ad esempio, generare ID con la stessa entropia di cui sopra ma utilizzando caratteri esadecimali:

import EntropyString

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

"135fe71aec7a80c02dce5"

Notare la differenza nella lunghezza della stringa dovuta alla differenza nel numero totale di caratteri nel set di caratteri utilizzato. Il rischio di ripetizione nel numero specificato di stringhe potenziali è lo stesso. Le lunghezze delle stringhe non lo sono. E soprattutto, il rischio di ripetizione e il potenziale numero di stringhe sono espliciti. Non dovrai più indovinare con la lunghezza della stringa.


2

Se la tua stringa casuale deve essere sicura in modo casuale, usa questo:

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

Se hai solo bisogno di un identificatore univoco, UUID().uuidStringpuò servire ai tuoi scopi.


1

Aggiornato per Swift 4. Utilizzare una variabile memorizzata lazy sull'estensione di classe. Questo viene calcolato solo una volta.

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

Utilizzando RandomNumberGenerator per prestazioni migliori come consigliato da Apple

Utilizzo: String.random(20) Risultato: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

Questa è la Swift soluzione -est ho potuto trovare. 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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.