Генерация случайной буквенно-цифровой строки в 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()! })
}

Обновление 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
}

Оригинальный ответ:

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)
Саймон Х

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

Swift:

let randomString = NSUUID().uuidString

UUID (). UuidString сейчас
Марк Бриджес

2
UUID иногда дает одну и ту же строку!
Бурак Озтюрк

2
Существуют различные типы UUID, некоторые из которых основаны на времени. В Swift Foundation используется случайный V4 . Существует очень мало шансов (например, невероятно мало, что один и тот же UUID будет сгенерирован дважды.
Робин Даугерти,

11

В 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! Я думал, что принудительное развертывание будет иметь лучшую производительность, чем использование CompactMap.
Легион

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, o, O, i и т. Д. ... символы, которые люди часто путают.

Это часто делается для кодов бронирования и аналогичных кодов, которые будут использовать клиенты-люди.


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

РЕДАКТИРОВАТЬ

Пожалуйста, не путайте с UIDevice.current.identifierForVendor?.uuidStringэтим не даст случайных значений.


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
}

В основном, вызовите этот метод, который сгенерирует случайную строку длиной целого числа, переданного функции. Чтобы изменить возможные символы, просто отредактируйте строку charactersString. Поддерживает символы Unicode тоже.

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


2
По какой-то прискорбной причине эта версия иногда даетEXC_BAD_INSTRUCTION
Джо

Эй, Джо, у тебя есть демо-код, который может воспроизвести эту ошибку?
Эрнест Каннингем

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

1
Посмотрите этот ТАК вопрос, чтобы
выяснить

Важно: 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 , где есть та же проблема.
Мартин Р

@MartinR спасибо за указание на это. я обновил свой ответ
S1LENT WARRIOR

5

Чистый Свифт, случайный 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

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

Проблема с ответами на вопросы «Мне нужны случайные строки» (на любом языке) состоит в том, что практически каждое решение использует некорректную первичную спецификацию длины строки . Сами вопросы редко показывают, зачем нужны случайные строки, но я бы сказал, что вам редко нужны случайные строки длиной, скажем, 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- лучшее решение, которое я мог придумать. 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.