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


当前回答

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

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

其他回答

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

免费循环,尽管它被限制在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

斯威夫特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)
}

回答“我需要随机字符串”问题的问题(无论哪种语言)实际上每个解决方案都使用了有缺陷的字符串长度的主要规范。问题本身很少揭示为什么需要随机字符串,但我想挑战你很少需要长度为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”

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

斯威夫特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
    }
}