Wygeneruj losowy ciąg alfanumeryczny w Swift

208

Jak wygenerować losowy ciąg alfanumeryczny w Swift?

Visnu
źródło

Odpowiedzi:

355

Aktualizacja Swift 4.2

Swift 4.2 wprowadził znaczną poprawę w radzeniu sobie z losowymi wartościami i elementami. Możesz przeczytać więcej o tych ulepszeniach tutaj . Oto metoda zredukowana do kilku wierszy:

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

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

Oryginalna odpowiedź:

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
}
iAhmed
źródło
1
Czy istnieje sposób na zmodyfikowanie powyższego w celu zapewnienia, że ​​wygenerowany ciąg alfanumeryczny ma tylko 6 lub 8 znaków?
ksa_coder
4
randomString (długość: 6) lub randomString (długość: 8)
Simon H
58

Oto gotowe do użycia rozwiązanie w składni Swiftier . Możesz go po prostu skopiować i wkleić:

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
}

Jeśli wolisz Framework, który ma również kilka przydatnych funkcji, możesz sprawdzić mój projekt HandySwift . Zawiera również piękne rozwiązanie dla losowych ciągów alfanumerycznych :

String(randomWithLength: 8, allowedCharactersType: .alphaNumeric) // => "2TgM5sUG"
Jeehut
źródło
49

Możesz użyć go również w następujący sposób:

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

Proste użycie:

let randomString = String.random()

Składnia 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
    }
}

Składnia 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
    }
}
Bartłomiej Semańczyk
źródło
29

Szybki:

let randomString = NSUUID().uuidString
Powiedziany
źródło
UUID (). UuidString teraz
Mark Bridges
2
UUID czasami podaje ten sam ciąg!
Burak Öztürk
2
Istnieją różne rodzaje identyfikatorów UUID, niektóre z nich są oparte na czasie. W Swift Foundation zastosowano losową wersję V4 . Jest bardzo mała szansa (jak, niewiarygodnie mało, że ten sam UUID zostanie wygenerowany dwukrotnie.
Robin Daugherty,
11

AKTUALIZACJA 2019.

W niezwykłym przypadku to

wydajność ma znaczenie.

Oto niezwykle przejrzysta funkcja buforująca :

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

Dzieje się tak, gdy masz ustalony, znany zestaw znaków.

Przydatna wskazówka:

Zauważ, że „abcdefghjklmnpqrstuvwxyz12345789” unika „złych” znaków

Nie ma 0, o, O, i itd. ... ludzie często mylą.

Odbywa się to często w przypadku kodów rezerwacji i podobnych kodów, które będą używane przez klientów.

Fattie
źródło
1
Głosowanie za repeating:count:.
Cœur
10

Prosty i szybki - UUID (). UuidString

// Zwraca ciąg utworzony na podstawie identyfikatora UUID, taki jak „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

EDYTOWAĆ

Proszę nie mylić z UIDevice.current.identifierForVendor?.uuidStringtym, że nie da losowych wartości.

Mohamed Jaleel Nazir
źródło
10

W Swift 4.2 najlepiej jest utworzyć ciąg znaków z wybranymi postaciami, a następnie użyć randomElement, aby wybrać każdą postać:

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

Więcej szczegółów na temat tych zmian tutaj .

leogdion
źródło
3
Zamiast mapy można użyć compactMap, a wtedy nie ma takiej potrzeby! operator. ;)
Kristaps Grinbergs
1
Hej @KristapsGrinbergs! Myślałem, że wymuszone rozpakowywanie będzie miało lepszą wydajność niż użycie compactMap.
leogdion
6

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

Zasadniczo wywołaj tę metodę, która wygeneruje losowy ciąg długości całkowitej liczby całkowitej przekazanej funkcji. Aby zmienić możliwe znaki, po prostu edytuj ciąg znakówString. Obsługuje również znaki Unicode.

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

Ernest Cunningham
źródło
2
Z jakiegoś niefortunnego powodu ta wersja czasami dajeEXC_BAD_INSTRUCTION
Joe
Hej Joe, czy masz kod demonstracyjny, który może odtworzyć ten błąd?
Ernest Cunningham
Zobaczę co da się zrobić; Nazwałem to tak, jak jest w sklepie akcji IB let random = randomString(16). EXC był tylko na prawdziwym urządzeniu i nie widziałem go w symulatorze i był przerywany na urządzeniu.
Joe
1
Zobacz to pytanie SO, aby
dowiedzieć się,
Ważne: random % count nie nie (zawsze) stworzyć jednolitą dystrybucję. Jeśli jest to istotne dla Ciebie, poszukaj innych odpowiedzi, które wykorzystują arc4random_uniform().
Raphael,
6

Dla osób, które nie chcą wpisywać całego zestawu znaków:

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)
}
fal
źródło
5

dla 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
}
S1LENT WARRIOR
źródło
1
Próbowałeś tego kodu? Czy zauważyłeś, że zwracana długość łańcucha jest niepoprawna i że są tylko cyfry? Spójrz na stackoverflow.com/q/39566062/1187415 , który ma ten sam problem.
Martin R
@MartinR dziękuję za zwrócenie na to uwagi. zaktualizowałem swoją odpowiedź
S1LENT WARRIOR,
5

Czysty losowy Stringz dowolnego CharacterSet.

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

Ta characters()funkcja jest moją najszybszą znaną implementacją .

Cœur
źródło
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
}
Daniel Howard
źródło
3

Moja jeszcze szybsza implementacja pytania:

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("")
}
Lachezar
źródło
3

Bez pętli, choć jest ograniczona do 43 znaków. Jeśli potrzebujesz więcej, możesz je zmodyfikować. Podejście to ma dwie zalety w porównaniu z wykorzystaniem wyłącznie UUID:

  1. Większa Entropia poprzez użycie małych liter, jak UUID() generuje tylko duże litery
  2. Długość A UUIDwynosi maksymalnie 36 znaków (w tym 4 łączniki), ale bez nich tylko 32 znaki. Jeśli potrzebujesz czegoś dłuższego lub nie chcesz dołączać łączników, skorzystaj z base64EncodedStringuchwytów

Ponadto funkcja ta wykorzystuje a, UIntaby uniknąć liczb ujemnych.

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

Wywołanie go w pętli w celu sprawdzenia wyjścia:

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

Który produkuje:

Nzk3NjgzMTdBQ0FBNDFCNzk2MDRENzZF
MUI5RURDQzE1RTdCNDA3RDg2MTI4QkQx
M0I3MjJBRjVFRTYyNDFCNkI5OUM1RUVC
RDA1RDZGQ0IzQjI1NDdGREI3NDgxM0Mx
NjcyNUQyOThCNzhCNEVFQTk1RTQ3NTIy
MDkwRTQ0RjFENUFGNEFDOTgyQTUxODI0
RDU2OTNBOUJGMDE4NDhEODlCNEQ1NjZG
RjM2MTUxRjM4RkY3NDU2OUFDOTI0Nzkz
QzUwOTE1N0U1RDVENDE4OEE5NTM2Rjcy
Nzk4QkMxNUJEMjYwNDJDQjhBQkY5QkY5
ODhFNjU0MDVEMUI2NEI5QUIyNjNCNkVF
CodeBender
źródło
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)
Sreekanth G.
źródło
2

Problem z odpowiedziami na pytania „Potrzebuję losowych ciągów” (w dowolnym języku) polega na tym, że praktycznie każde rozwiązanie wykorzystuje wadliwą podstawową specyfikację długości łańcucha . Same pytania rzadko ujawniają, dlaczego potrzebne są losowe ciągi, ale rzuciłbym wyzwanie, że rzadko potrzebujesz losowych ciągów, powiedzmy 8. To, czego zawsze potrzebujesz, to pewna liczba unikatowych ciągów , na przykład do użycia jako identyfikatory w jakimś celu.

Istnieją dwa wiodące sposoby uzyskania ściśle unikatowych ciągów: deterministycznie (co nie jest losowe) i przechowywanie / porównywanie (co jest uciążliwe). Co robimy? Porzucamy ducha. Idziemy z probabilistycznego wyjątkowości zamiast. To znaczy, akceptujemy, że istnieje pewne (choć niewielkie) ryzyko, że nasze łańcuchy nie będą unikalne. To tutaj rozumiemy prawdopodobieństwo kolizji i entropię pomocne .

Dlatego zmienię niezmienną potrzebę jako wymagającą pewnej liczby ciągów z niewielkim ryzykiem powtórzenia. Jako konkretny przykład załóżmy, że chcesz wygenerować potencjał 5 milionów identyfikatorów. Nie chcesz przechowywać i porównywać każdego nowego łańcucha i chcesz, aby były losowe, więc akceptujesz ryzyko powtórzenia. Jako przykład, powiedzmy, że ryzyko jest mniejsze niż 1 przy trylionach szansy na powtórzenie. Jakiej długości sznurka potrzebujesz? To pytanie jest nieokreślone, ponieważ zależy od użytych znaków. Ale co ważniejsze, jest to mylące. Potrzebujesz specyfikacji entropii łańcuchów, a nie ich długości. Entropia może być bezpośrednio związana z prawdopodobieństwem powtórzenia w pewnej liczbie ciągów. Długość łańcucha nie może.

I tu może pomóc biblioteka taka jak EntropyString . Aby wygenerować losowe identyfikatory, które mają mniej niż 1, z bilionem szansy na powtórzenie w 5 milionach ciągów przy użyciu EntropyString:

import EntropyString

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

„Rrrj6pN4d6GBrFLH4”

EntropyStringużywa domyślnie zestawu znaków zawierającego 32 znaki. Istnieją inne predefiniowane zestawy znaków, a także możesz określić własne znaki. Na przykład generowanie identyfikatorów z taką samą entropią jak powyżej, ale przy użyciu znaków szesnastkowych:

import EntropyString

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

„135fe71aec7a80c02dce5”

Zwróć uwagę na różnicę długości łańcucha wynikającą z różnicy w całkowitej liczbie znaków w użytym zestawie znaków. Ryzyko powtórzenia w określonej liczbie potencjalnych ciągów jest takie samo. Długości łańcucha nie są. A co najważniejsze, ryzyko powtórzenia i potencjalna liczba ciągów jest wyraźna. Nigdy więcej zgadywania przy długości łańcucha.

niebo dingo
źródło
2

Jeśli ciąg losowy powinien być bezpieczny-losowy, użyj tego:

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
}
schirrmacher
źródło
1

Jeśli potrzebujesz tylko unikalnego identyfikatora, UUID().uuidStringmoże służyć Twoim celom.

man1
źródło
1

Zaktualizowano dla Swift 4. Użyj leniwej zmiennej przechowywanej w rozszerzeniu klasy. Oblicza się to tylko raz.

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
Kawin P.
źródło
1

SWIFT 4

Korzystanie z RandomNumberGenerator dla lepszej wydajności jako rekomendacji Apple

Zastosowanie: String.random(20) Wynik: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
    }
}
Jad
źródło
0

To Swift -est rozwiązanie mogłem wymyślić. 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
    } 
}
bojan4
źródło
-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
}
SteMa
źródło