Створити випадкову буквено-числову рядок у Swift


208

Як я можу генерувати випадкову буквено-цифрову рядок у Swift?

Відповіді:


355

Оновлення Swift 4.2

Swift 4.2 представив основні вдосконалення в роботі зі випадковими значеннями та елементами. Більше про ці вдосконалення ви можете прочитати тут . Ось метод, зведений до кількох рядків:

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

Швидке оновлення 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
}

Оригінальна відповідь:

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
Чи є спосіб я змінити вищезазначене, щоб гарантувати, що згенеровано буквено-цифровий рядок має довжину лише 6 або 8 символів?
ksa_coder

4
randomString (довжина: 6) або randomString (довжина: 8)
Simon H

58

Ось готове до використання рішення у синтаксисі Swiftier . Ви можете просто скопіювати та вставити:

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
}

Якщо ви віддаєте перевагу Framework, який також має деякі зручні функції, не соромтеся замовити мій проект HandySwift . Він також включає прекрасне рішення для випадкових буквено-цифрових рядків :

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

49

Ви можете використовувати його також наступним чином:

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

Просте використання:

let randomString = String.random()

Синтаксис 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
    }
}

Синтаксис 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
    }
}

29

Швидкий:

let randomString = NSUUID().uuidString

UUID (). UuidString now
Mark Bridges

2
UUID іноді дає ту саму рядок!
Burak Öztürk

2
Існують різні види UUID, деякі з них засновані на часі. Той, який використовується у Фонді Свіфт, - V4, випадковий . Є дуже мало шансів (наприклад, неймовірно мало того, що той самий UUID буде генеруватися двічі.
Robin Daugherty,

11

ОНОВЛЕНО 2019 рік.

У незвичному випадку це

Виконання питань.

Ось надзвичайно чітка функція, яка кешує :

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

Це для тих випадків, коли у вас є фіксований, відомий набір символів.

Зручна порада:

Зауважте, що "abcdefghjklmnpqrstuvwxyz12345789" уникає "поганих" символів

Немає 0, о, О, я і т. Д. ... символи, які люди часто плутають.

Це часто робиться для кодів бронювання та подібних кодів, якими користуватимуться людські клієнти.


1
Оголошення для repeating:count:.
Cœur

10

Простий і швидкий - UUID (). UuidString

// Повертає рядок, створений з UUID, наприклад "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

EDIT

Будь ласка , не плутайте з UIDevice.current.identifierForVendor?.uuidStringнею не даватиме випадкові значення.


10

Завдяки Swift 4.2 найкраще створити рядок з потрібними символами, а потім використовувати randomElement для вибору кожного символу:

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

Я детальніше про ці зміни тут .


3
Замість карти ви можете використовувати compactMap, і тоді в цьому немає необхідності! оператор. ;)
Крістапс Грінбергс

1
Привіт @KristapsGrinbergs! Думала, що примусове розгортання матиме кращі показники, ніж використання компактної карти.
leogdion

6

Версія 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
}

В основному виклик цього методу, який буде генерувати випадкову рядок довжиною цілого числа, переданого функції. Для зміни можливих символів просто відредагуйте рядок символівСтринг. Також підтримуються символи Unicode.

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


2
Чомусь ця версія періодично видаєEXC_BAD_INSTRUCTION
Джо

Ей Джо, у тебе є демо-код, який може відтворити цю помилку?
Ернест Каннінгем,

Дозвольте мені побачити, що я можу зробити; Я лише називав це таким, що знаходиться у відділенні дій IB let random = randomString(16). EXC був лише на реальному пристрої, і я не бачив його в тренажері, і він переривався на пристрої.
Джо

1
Дивіться це питання SO з тієї причини , чому це відбувається збій половину часу на 32-розрядних пристроїв: stackoverflow.com/questions/25274265 / ...
julien_c

Важливо: random % count це НЕ (завжди) створюють однорідний розподіл. Якщо це стосується вас, перегляньте інші відповіді, які використовують arc4random_uniform().
Рафаель

6

Для людей, які не хочуть набирати весь набір символів:

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

для 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
Ви спробували цей код? Ви помітили, що повернута довжина рядка неправильна і що є лише цифри? Погляньте на stackoverflow.com/q/39566062/1187415 , який має ту саму проблему.
Martin R

@MartinR дякую, що вказали на це. я оновив свою відповідь
S1LENT WARRIOR

5

Чистий Swift випадковий Stringвід будь-якого CharacterSet.

Використання: 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
    }
}

characters()Функція мій найшвидший з відомих реалізації .


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

Моя ще більш швидка реалізація питання:

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

Без циклу, хоча він обмежений 43 символами. Якщо вам потрібно більше, його можна змінити. Цей підхід має дві переваги перед виключно використанням UUID:

  1. Більша ентропія, використовуючи малі літери, оскільки UUID()створює лише великі літери
  2. Довжина "A" UUID- це не більше 36 символів (включаючи 4 дефіси), але лише 32 символи. Якщо вам потрібно щось довше, або ви не хочете, щоб дефіси були включені, використання base64EncodedStringручок це

Також ця функція використовує а, UIntщоб уникнути від'ємних чисел.

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

Викликаючи його в циклі, щоб перевірити вихід:

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

Що виробляє:

Nzk3NjgzMTdBQ0FBNDFCNzk2MDRENzZF
MUI5RURDQzE1RTdCNDA3RDg2MTI4QkQx
M0I3MjJBRjVFRTYyNDFCNkI5OUM1RUVC
RDA1RDZGQ0IzQjI1NDdGREI3NDgxM0Mx
NjcyNUQyOThCNzhCNEVFQTk1RTQ3NTIy
MDkwRTQ0RjFENUFGNEFDOTgyQTUxODI0
RDU2OTNBOUJGMDE4NDhEODlCNEQ1NjZG
RjM2MTUxRjM4RkY3NDU2OUFDOTI0Nzkz
QzUwOTE1N0U1RDVENDE4OEE5NTM2Rjcy
Nzk4QkMxNUJEMjYwNDJDQjhBQkY5QkY5
ODhFNjU0MDVEMUI2NEI5QUIyNjNCNkVF

3

Швидкий 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

Проблема з відповідями на запитання "Мені потрібні випадкові рядки" (будь-якою мовою) практично в кожному рішенні використовується хибна первинна специфікація довжини рядка . Самі запитання рідко виявляють, для чого потрібні випадкові рядки, але я б заперечував, що вам рідко потрібні випадкові рядки довжиною, скажімо 8. Те, що вам незмінно потрібно, це деяка кількість унікальних рядків , наприклад, щоб використовувати як ідентифікатори для певної мети.

Є два провідні способи отримання строго унікальних рядків: детерміновано (що не випадково) та зберігати / порівнювати (що є обтяжливим). Що ми робимо? Ми відмовляємося від привида. Натомість ми йдемо з імовірнісною унікальністю . Тобто ми приймаємо, що існує певний (хоча і малий) ризик того, що наші струни не будуть унікальними. Тут допомагають розуміння ймовірності зіткнення та ентропії .

Тож я перефразую незмінну потребу як потрібну деяку кількість рядків з невеликим ризиком повторити. В якості конкретного прикладу, скажімо, ви хочете створити потенціал у 5 мільйонів ідентифікаторів. Ви не хочете зберігати та порівнювати кожен новий рядок, а ви хочете, щоб вони були випадковими, тому ви приймаєте певний ризик повторення. Наприклад, скажімо, ризик менший ніж 1 на трильйон шансів повторити. Отже, яка довжина струни вам потрібна? Ну, це питання не визначено, оскільки це залежить від використовуваних персонажів. Але що ще важливіше - це помилково. Вам потрібно конкретизувати ентропію струн, а не їх довжину. Ентропія може бути безпосередньо пов'язана з ймовірністю повторення в деякій кількості рядків. Довжина рядка не може.

І тут може допомогти бібліотека на зразок EntropyString . Для створення випадкових ідентифікаторів, які мають менше 1 трильйона шансів повторити в 5 мільйонах рядків, використовуючи EntropyString:

import EntropyString

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

"Rrrj6pN4d6GBrFLH4"

EntropyStringвикористовує набір символів з 32 символами за замовчуванням. Є й інші заздалегідь задані набори символів, і ви можете також вказати власні символи. Наприклад, генерування ідентифікаторів з тією ж ентропією, що описана вище, але з використанням шістнадцяткових символів:

import EntropyString

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

"135fe71aec7a80c02dce5"

Зверніть увагу на різницю довжини рядка через різницю в загальній кількості символів у використаному наборі символів. Ризик повторення у вказаній кількості потенційних рядків однаковий. Довжина рядків не є. І найкраще, що ризик повторення та потенційна кількість рядків є явним. Більше не вгадуйте з довжиною рядка.


2

Якщо ваш випадковий рядок повинен бути захищеним-випадковим, скористайтеся цим:

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

Якщо вам просто потрібен унікальний ідентифікатор, він UUID().uuidStringможе служити вашим цілям.


1

Оновлено для Swift 4. Використовуйте ледачу збережену змінну на розширенні класу. Це обчислюється лише один раз.

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

Використання RandomNumberGenerator для кращої продуктивності як рекомендація Apple

Використання: String.random(20) Результат: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

Це Swift -est рішення , яке я міг придумати. 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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.