我如何在Swift中生成一个随机的字母数字字符串?


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
}

这里有一个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
}

如果你喜欢一个框架,也有一些更方便的功能,然后随时签出我的项目handysswift。它还包括一个漂亮的随机字母数字字符串的解决方案:

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

我对这个问题的更快速的回答是:

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

你也可以这样使用它:

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

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


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

如果您只需要一个唯一标识符UUID()。uuidString可以满足您的需求。


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
}

这是我能想到的最快的解决办法。斯威夫特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
    } 
}

来自任何字符集的纯Swift随机字符串。

用法:CharacterSet.alphanumerics。randomString(长度: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()函数是我所知道的最快的实现。


更新后的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等等…人类经常混淆的字符。

这通常用于预订代码和人类客户将使用的类似代码。


简单快捷——UUID().uuidString

//返回由UUID创建的字符串,例如"E621E1F8-C36C-495A-93FC-0C247A3E6E5F" uuidString:字符串{get} https://developer.apple.com/documentation/foundation/uuid

斯威夫特3.0

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

EDIT

请不要与UIDevice.current.identifierForVendor混淆。uuidString它不会给出随机值。


迅速:

let randomString = NSUUID().uuidString

回答“我需要随机字符串”问题的问题(无论哪种语言)实际上每个解决方案都使用了有缺陷的字符串长度的主要规范。问题本身很少揭示为什么需要随机字符串,但我想挑战你很少需要长度为8的随机字符串。您总是需要一些唯一的字符串,例如,用于某种目的的标识符。

有两种主要的方法来获得严格唯一的字符串:确定性(不是随机的)和存储/比较(繁琐的)。我们该怎么办?我们放弃了。我们用概率唯一性来代替。也就是说,我们接受存在一些(无论多么小)的风险,即我们的字符串不是唯一的。这就是为什么理解碰撞概率和熵是有帮助的。

So I'll rephrase the invariable need as needing some number of strings with a small risk of repeat. As a concrete example, let's say you want to generate a potential of 5 million IDs. You don't want to store and compare each new string, and you want them to be random, so you accept some risk of repeat. As example, let's say a risk of less than 1 in a trillion chance of repeat. So what length of string do you need? Well, that question is underspecified as it depends on the characters used. But more importantly, it's misguided. What you need is a specification of the entropy of the strings, not their length. Entropy can be directly related to the probability of a repeat in some number of strings. String length can't.

这就是像EntropyString这样的库可以帮助的地方。使用EntropyString在500万个字符串中生成重复概率小于1万亿分之一的随机id:

import EntropyString

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

" Rrrj6pN4d6GBrFLH4 "

EntropyString默认使用32个字符的字符集。还有其他预定义的字符集,您也可以指定自己的字符。例如,生成与上面相同熵的id,但使用十六进制字符:

import EntropyString

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

“135fe71AEC7A80C02DCE5”

请注意由于所使用的字符集中字符总数的不同而导致的字符串长度的差异。在指定数量的潜在字符串中重复的风险相同。字符串长度不是。最重要的是,重复的风险和字符串的潜在数量是明确的。没有更多的猜测字符串长度。


如果你的随机字符串应该是安全随机的,使用这个:

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
}

在Swift 4.2中,你最好的方法是创建一个包含你想要的字符的字符串,然后使用randomElement来选择每个字符:

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

我将在这里详细介绍这些变化。


为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

免费循环,尽管它被限制在43个字符。如果你需要更多,可以修改。与单独使用UUID相比,这种方法有两个优点:

“更大的熵”使用小写字母,因为UUID()只生成大写字母 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

斯威夫特4

苹果推荐使用RandomNumberGenerator获得更好的性能

用法: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
    }
}

一种避免输入整套字符的方法:

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

SwifterSwift有这个实现

/// SwifterSwift: Create a new random string of given length.
///
///     String(randomOfLength: 10) -> "gY8r3MHvlQ"
///
/// - Parameter length: number of characters in string.
init(randomOfLength length: Int) {
    guard length > 0 else {
        self.init()
        return
    }

    let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    var randomString = ""
    for _ in 1...length {
        randomString.append(base.randomElement()!)
    }
    self = randomString
}

我做了一些改变,并使用这个实现

static func random(randomOfLength length: Int) -> String {
    guard length > 0 else { return "" }
    let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    var randomString = ""
    for _ in 1...length {
        guard let randomCharacter = base.randomElement() else { continue }
        randomString.append(randomCharacter)
    }
    return randomString
}

斯威夫特5.6

此函数生成一个以36为基数的10位数字,然后将其作为字母数字字符串返回。

func randomCode(length: Int) -> String {
    let radix = 36 // = 10 digits + 26 letters
    let number = Int.random(in: 0..<(pow(radix, length)))
    return String(number, radix: radix, uppercase: true)
}

或者如果你不希望代码以“0”开头:

func randomCode(length: Int) -> String {
    let radix = 36 // = 10 digits + 26 letters
    let range = (pow(radix, length)/2)..<(pow(radix, length))
    let number = Int.random(in: range)
    return String(number, radix: radix, uppercase: true)
}