如何在Go中生成固定长度的随机字符串?

时间:2014-04-06 09:22:54

标签: string random go

我想在Go中只使用随机字符串(大写或小写),没有数字。什么是最快最简单的方法?

17 个答案:

答案 0 :(得分:618)

Paul的解决方案提供了一个简单的,通用解决方案。

这个问题要求"最快最简单的方式" 。让我们来解决最快的部分。我们将以迭代的方式获得最终,最快的代码。可以在答案的最后找到每次迭代的基准测试。

所有解决方案和基准测试代码均可在Go Playground上找到。 Playground上的代码是测试文件,而不是可执行文件。您必须将其保存到名为XX_test.go的文件中并使用

运行它
go test -bench . -benchmem

<强>前言

  

如果您只需要一个随机字符串,那么最快的解决方案不是首选解决方案。为此,保罗的解决方案是完美的。如果性能确实很重要。虽然前两个步骤(字节剩余)可能是一个可接受的折衷方案:它们确实提高了50%的性能(请参阅 II中的确切数字。基准测试部分),并且它们不会显着增加复杂性。

话虽如此,即使你不需要最快的解决方案,阅读这个答案可能是冒险和教育。

予。改进

1。创世纪(符文)

提醒一下,我们正在改进的原始通用解决方案是:

func init() {
    rand.Seed(time.Now().UnixNano())
}

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func RandStringRunes(n int) string {
    b := make([]rune, n)
    for i := range b {
        b[i] = letterRunes[rand.Intn(len(letterRunes))]
    }
    return string(b)
}

2。字节

如果要选择的字符和汇编随机字符串只包含英文字母的大写和小写字母,我们只能使用字节,因为英文字母字母映射到UTF-8中的字节1对1编码(这是Go存储字符串的方式)。

所以而不是:

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

我们可以使用:

var letters = []bytes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

甚至更好:

const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

现在这已经是一个很大的进步:我们可以将其变为conststring常量,但there are no slice constants)。作为额外收益,表达式len(letters)也将是const! (如果len(s)是一个字符串常量,则表达式s是常量。)

费用是多少?什么都没有。 string可以被索引,索引其字节,完美,正是我们想要的。

我们的下一个目的地如下:

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func RandStringBytes(n int) string {
    b := make([]byte, n)
    for i := range b {
        b[i] = letterBytes[rand.Intn(len(letterBytes))]
    }
    return string(b)
}

3。其余

以前的解决方案会通过调用rand.Intn()来指定一个随机数字来指定一个随机字母,Rand.Intn()委托给Rand.Int31n() rand.Int63()

与产生63个随机位的随机数的crypto/rand相比,这要慢得多。

因此我们可以简单地调用rand.Int63()并在除以len(letterBytes)之后使用余数:

func RandStringBytesRmndr(n int) string {
    b := make([]byte, n)
    for i := range b {
        b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))]
    }
    return string(b)
}

这种方法效果明显更快,缺点是所有字母的概率都不完全相同(假设rand.Int63()产生所有63位数的概率相等)。虽然失真非常小,因为字母52的数量远小于1<<63 - 1,所以在实践中这非常好。

为了让这个理解更容易:让我们说你想要一个0..5范围内的随机数。使用3个随机位,这将产生具有双倍概率的数字0..1,而不是范围2..5。使用5个随机位,范围0..1中的数字将以6/32概率出现,而2..5范围内的数字具有5/32概率,现在更接近期望值。增加位数会降低这一点,当达到63位时,它可以忽略不计。

4。掩蔽

在前面的解决方案的基础上,我们可以通过使用随机数的最低位来保持字母的平均分配,因为需要许多字母来表示字母数。因此,例如,如果我们有52个字母,则需要6位来表示它:52 = 110100b。因此,我们只使用rand.Int63()返回的最低6位数。为了保持信件的平等分配,我们只接受&#34;该数字是否在0..len(letterBytes)-1范围内。如果最低位更大,我们将其丢弃并查询新的随机数。

请注意,最低位大于或等于len(letterBytes)的可能性一般小于0.5(平均0.25),这意味着即使这样是这样,重复这个&#34;罕见&#34;案件减少了找不到好号码的机会。重复n后,我们没有获得良好索引的可能性远低于pow(0.5, n),这只是一个较高的估计值。在52个字母的情况下,6个最低位不好的可能性仅为(64-52)/64 = 0.19;这意味着,例如,重复10次后没有好数字的机会是1e-8

所以这是解决方案:

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
    letterIdxBits = 6                    // 6 bits to represent a letter index
    letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
)

func RandStringBytesMask(n int) string {
    b := make([]byte, n)
    for i := 0; i < n; {
        if idx := int(rand.Int63() & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i++
        }
    }
    return string(b)
}

5。掩蔽改进

之前的解决方案仅使用rand.Int63()返回的63个随机位中最低的6位。这是一种浪费,因为获取随机位是我们算法中最慢的部分。

如果我们有52个字母,则表示6位代码为字母索引。因此63个随机位可以指定63/6 = 10个不同的字母索引。让我们使用所有那些10:

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
    letterIdxBits = 6                    // 6 bits to represent a letter index
    letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
    letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
)

func RandStringBytesMaskImpr(n int) string {
    b := make([]byte, n)
    // A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
    for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = rand.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i--
        }
        cache >>= letterIdxBits
        remain--
    }

    return string(b)
}

6。源

屏蔽改进非常好,我们无法改进它。我们可以,但不值得复杂。

现在让我们找一些其他方法来改进。 随机数的来源。

有一个Read(b []byte)包提供rand.Source函数,因此我们可以使用它来获得所需数量的单个调用的字节数。这在性能方面没有帮助,因为crypto/rand实现了加密安全的伪随机数生成器,因此速度要慢得多。

所以让我们坚持math/rand包。 rand.Rand使用How to convert utf8 string to []byte?作为随机位的来源。 rand.Source是一个指定Int63() int64方法的接口:我们在最新解决方案中唯一需要和使用的方法。

因此,我们确实不需要rand.Rand(显式或全局,共享的rand包),rand.Source对我们来说已经足够了:< / p>

var src = rand.NewSource(time.Now().UnixNano())

func RandStringBytesMaskImprSrc(n int) string {
    b := make([]byte, n)
    // A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = src.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i--
        }
        cache >>= letterIdxBits
        remain--
    }

    return string(b)
}

另请注意,最后一个解决方案并不要求您初始化(种子)Rand包的全局math/rand,因为未使用(我们的rand.Source是正确初始化/播种)。

此处还需要注意的一件事:math/rand州的包文档:

  

默认Source对于多个goroutine并发使用是安全的。

因此默认来源比Source可能获得的rand.NewSource()慢,因为默认来源必须在并发访问/使用时提供安全性,而rand.NewSource()不提供这个(因此它返回的Source更可能更快)。

7。利用strings.Builder

以前的所有解决方案都会返回string,其内容首先在切片中构建({strong> Genesis 中为[]rune,后续解决方案中为[]byte),然后转换为string。最终转换必须复制切片的内容,因为string值是不可变的,如果转换不能复制,则无法保证字符串的内容未通过其原始切片进行修改。有关详细信息,请参阅golang: []byte(string) vs []byte(*string)Go 1.10 introduced strings.Builder.

strings.Builder bytes.Buffer我们可以使用一种新类型来构建与Builder.String()类似的string内容。它使用[]byte在内部执行,当我们完成后,我们可以使用其Builder.Grow()方法获取最终string值。但其中很酷的是它没有执行我们刚才谈到的副本就能做到这一点。它敢于这样做,因为用于构建字符串内容的字节切片没有暴露,所以保证没有人可以无意或恶意地修改它来改变生成的&#34; immutable&#34;字符串。

所以我们的下一个想法是不在一个切片中构建随机字符串,但是在strings.Builder的帮助下,所以一旦我们完成,我们就可以获得并返回结果,而不必制作它的副本。这在速度方面可能有所帮助,在内存使用和分配方面肯定会有所帮助。

func RandStringBytesMaskImprSrcSB(n int) string {
    sb := strings.Builder{}
    sb.Grow(n)
    // A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = src.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
            sb.WriteByte(letterBytes[idx])
            i--
        }
        cache >>= letterIdxBits
        remain--
    }

    return sb.String()
}

请注意,在创建新的strings.Buidler之后,我们调用了它的unsafe方法,确保它分配了足够大的内部切片(以避免在添加随机字母时重新分配)。

8。 &#34; Mimicing&#34; strings.Builderunsafe

strings.Builder在内部[]byte中构建字符串,与我们自己一样。因此,基本上通过strings.Builder进行操作会产生一些开销,我们切换到strings.Builder的唯一办法是避免最终复制切片。

strings.Builder使用包Go 1.7 added避免了最终副本:

// String returns the accumulated string.
func (b *Builder) String() string {
    return *(*string)(unsafe.Pointer(&b.buf))
}

问题是,我们也可以自己做。所以这里的想法是切换回[]byte中构建随机字符串,但是当我们完成后,不要将其转换为string以返回,但做不安全转换:获取一个string,它指向我们的字节切片作为字符串数据。

这是如何做到的:

func RandStringBytesMaskImprSrcUnsafe(n int) string {
    b := make([]byte, n)
    // A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = src.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i--
        }
        cache >>= letterIdxBits
        remain--
    }

    return *(*string)(unsafe.Pointer(&b))
}

(9.使用rand.Read()

rand.Read() Rand.Read()函数和github.com/icza/bitio方法。我们应该尝试使用它们在一个步骤中读取所需的字节数,以便获得更好的性能。

有一个小问题&#34;有这个:我们需要多少字节?我们可以说:输出字母数量多。我们认为这是一个较高的估计,因为字母索引使用少于8位(1字节)。但是在这一点上,我们已经做得更糟了(因为获取随机位是&#34;困难部分&#34;),而且我们得到的不仅仅是需要。

另请注意,为了保持所有字母索引的平均分配,可能会有一些&#34;垃圾&#34;我们无法使用的随机数据,因此我们最终会跳过一些数据,因此当我们遍历所有字节切片时最终会缩短。我们需要进一步获得更多随机字节,&#34;递归&#34;。现在我们甚至失去了对rand包裹的单一呼叫#34;优点...

我们可以&#34;有点&#34;优化我们从math.Rand()获取的随机数据的使用情况。我们可以估计我们需要多少字节(比特)。 1个字母需要letterIdxBits位,我们需要n个字母,因此我们需要n * letterIdxBits / 8.0个字节向上舍入。我们可以计算随机索引不可用的概率(参见上文),因此我们可以请求更多可能&#34;更可能&#34;足够了(如果事实证明它不是,我们重复这个过程)。我们可以将字节片处理为&#34;比特流&#34;例如,我们有一个很好的第三方库:{{3}}(披露:我是作者)。

但Benchmark代码仍显示我们没有获胜。为什么会这样?

最后一个问题的答案是因为rand.Read()使用了一个循环并一直调用Source.Int63(),直到它填满传递的切片。正是RandStringBytesMaskImprSrc()解决方案的作用,没有中间缓冲区,并且没有增加复杂性。这就是RandStringBytesMaskImprSrc()留在宝座上的原因。是的,RandStringBytesMaskImprSrc()使用与rand.Source不同的非同步rand.Read()。但推理仍然适用;如果我们使用Rand.Read()代替rand.Read()(前者也是非同步的),则证明了这一点。

II。基准

好的,是时候对不同的解决方案进行基准测试了。

关键时刻:

BenchmarkRunes-4                     2000000    723 ns/op   96 B/op   2 allocs/op
BenchmarkBytes-4                     3000000    550 ns/op   32 B/op   2 allocs/op
BenchmarkBytesRmndr-4                3000000    438 ns/op   32 B/op   2 allocs/op
BenchmarkBytesMask-4                 3000000    534 ns/op   32 B/op   2 allocs/op
BenchmarkBytesMaskImpr-4            10000000    176 ns/op   32 B/op   2 allocs/op
BenchmarkBytesMaskImprSrc-4         10000000    139 ns/op   32 B/op   2 allocs/op
BenchmarkBytesMaskImprSrcSB-4       10000000    134 ns/op   16 B/op   1 allocs/op
BenchmarkBytesMaskImprSrcUnsafe-4   10000000    115 ns/op   16 B/op   1 allocs/op

只需从符文切换到字节,我们就会立即获得 24%的性能提升,并且内存要求会降至三分之一

取消rand.Intn()并使用rand.Int63()代替另一个 20%提升。

屏蔽(在大指数的情况下重复)减慢一点(由于重复调用): -22% ......

但是当我们使用63个随机位中的所有(或大多数)时(来自一个rand.Int63()调用的10个索引):这会加快时间: 3次

如果我们使用(非默认,新)rand.Source代替rand.Rand,我们再次获得 21%。

如果我们使用strings.Builder,我们在速度中获得 3.5%,但我们也实现了 50%内存使用和分配!太好了!

最后,如果我们敢于使用包unsafe而不是strings.Builder,我们会再次获得 14%

将最终解决方案与初始解决方案进行比较:RandStringBytesMaskImprSrcUnsafe() RandStringRunes()快<6.3> ,使用六分之一内存和一半尽可能少的分配。完成任务。

答案 1 :(得分:107)

你可以为它编写代码。如果你想在UTF-8编码时依赖所有单字节的字母,这个代码可以更简单。

package main

import (
    "fmt"
    "time"
    "math/rand"
)

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func randSeq(n int) string {
    b := make([]rune, n)
    for i := range b {
        b[i] = letters[rand.Intn(len(letters))]
    }
    return string(b)
}

func main() {
    rand.Seed(time.Now().UnixNano())

    fmt.Println(randSeq(10))
}

答案 2 :(得分:16)

两种可能的选择(当然可能还有更多):

  1. 您可以使用支持读取随机字节数组的crypto/rand包(来自/ dev / urandom),并且适用于加密随机生成。见http://golang.org/pkg/crypto/rand/#example_Read。但它可能比正常的伪随机数生成慢。

  2. 取一个随机数并使用md5或类似的东西哈希。

答案 3 :(得分:10)

使用包uniuri,它会生成加密安全的统一(无偏)字符串。

答案 4 :(得分:3)

关注icza's奇妙解释的解决方案,以下是使用crypto/rand代替math/rand的修改。

const (
    letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // 52 possibilities
    letterIdxBits = 6                    // 6 bits to represent 64 possibilities / indexes
    letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
)

func SecureRandomAlphaString(length int) string {

    result := make([]byte, length)
    bufferSize := int(float64(length)*1.3)
    for i, j, randomBytes := 0, 0, []byte{}; i < length; j++ {
        if j%bufferSize == 0 {
            randomBytes = SecureRandomBytes(bufferSize)
        }
        if idx := int(randomBytes[j%length] & letterIdxMask); idx < len(letterBytes) {
            result[i] = letterBytes[idx]
            i++
        }
    }

    return string(result)
}

// SecureRandomBytes returns the requested number of bytes using crypto/rand
func SecureRandomBytes(length int) []byte {
    var randomBytes = make([]byte, length)
    _, err := rand.Read(randomBytes)
    if err != nil {
        log.Fatal("Unable to generate random bytes")
    }
    return randomBytes
}

如果你想要一个更通用的解决方案,它允许你传入一个字符字节片段来创建字符串,你可以尝试使用它:

// SecureRandomString returns a string of the requested length,
// made from the byte characters provided (only ASCII allowed).
// Uses crypto/rand for security. Will panic if len(availableCharBytes) > 256.
func SecureRandomString(availableCharBytes string, length int) string {

    // Compute bitMask
    availableCharLength := len(availableCharBytes)
    if availableCharLength == 0 || availableCharLength > 256 {
        panic("availableCharBytes length must be greater than 0 and less than or equal to 256")
    }
    var bitLength byte
    var bitMask byte
    for bits := availableCharLength - 1; bits != 0; {
        bits = bits >> 1
        bitLength++
    }
    bitMask = 1<<bitLength - 1

    // Compute bufferSize
    bufferSize := length + length / 3

    // Create random string
    result := make([]byte, length)
    for i, j, randomBytes := 0, 0, []byte{}; i < length; j++ {
        if j%bufferSize == 0 {
            // Random byte buffer is empty, get a new one
            randomBytes = SecureRandomBytes(bufferSize)
        }
        // Mask bytes to get an index into the character slice
        if idx := int(randomBytes[j%length] & bitMask); idx < availableCharLength {
            result[i] = availableCharBytes[idx]
            i++
        }
    }

    return string(result)
}

如果您想传递自己的随机来源,修改上述内容以接受io.Reader而非使用crypto/rand将是微不足道的。

答案 5 :(得分:2)

另一个版本,灵感来自generate password in JavaScript crypto

package main

import (
    "crypto/rand"
    "fmt"
)

var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-"

func shortID(length int) string {
    ll := len(chars)
    b := make([]byte, length)
    rand.Read(b) // generates len(b) random bytes
    for i := 0; i < length; i++ {
        b[i] = chars[int(b[i])%ll]
    }
    return string(b)
}

func main() {
    fmt.Println(shortID(18))
    fmt.Println(shortID(18))
    fmt.Println(shortID(18))
}

答案 6 :(得分:0)

这是我用来在我的应用程序中生成证书编号的示例代码。

func GenerateCertificateNumber() string {
    CertificateLength := 7
    t := time.Now().String()
    CertificateHash, err := bcrypt.GenerateFromPassword([]byte(t), bcrypt.DefaultCost)
    if err != nil {
        fmt.Println(err)
    }
    // Make a Regex we only want letters and numbers
    reg, err := regexp.Compile("[^a-zA-Z0-9]+")
    if err != nil {
        log.Fatal(err)
    }
    processedString := reg.ReplaceAllString(string(CertificateHash), "")
    fmt.Println(string(processedString))

    CertificateNumber := strings.ToUpper(string(processedString[len(processedString)-CertificateLength:]))
    fmt.Println(CertificateNumber)
    return CertificateNumber
}

答案 7 :(得分:0)

简单的解决方案,重复结果最少:

import (
    "math/rand"
    "time"
)

func randomString(length int) string {
    rand.Seed(time.Now().UnixNano())
    b := make([]byte, length)
    rand.Read(b)
    return fmt.Sprintf("%x", b)[:length]
}

PlayGround

中查看

答案 8 :(得分:0)

这是一种用于加密安全的随机字符串的简单高效的解决方案。

package main

import (
    "crypto/rand"
    "unsafe"
    "fmt"
)

var alphabet = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func main() {
    fmt.Println(generate(16))
}

func generate(size int) string {
    b := make([]byte, size)
    rand.Read(b)
    for i := 0; i < size; i++ {
        b[i] = alphabet[b[i] / 5]
    }
    return *(*string)(unsafe.Pointer(&b))
}

基准

Benchmark  95.2 ns/op      16 B/op      1 allocs/op

答案 9 :(得分:0)

func Rand(n int) (str string) {
    b := make([]byte, n)
    rand.Read(b)
    str = fmt.Sprintf("%x", b)
    return
}

答案 10 :(得分:0)

如果您想要具有加密安全性的随机数,并且确切的字符集是灵活的(例如,base64很好),则可以从所需的输出大小中准确计算出所需的随机字符长度。

基于64位的文本比基于256位的文本长1/3。(2 ^ 8与2 ^ 6; 8位/ 6位= 1.333比率)

import (
    "crypto/rand"
    "encoding/base64"
    "math"
)

func randomBase64String(l int) string {
    buff := make([]byte, int(math.Round(float64(l)/float64(1.33333333333))))
    rand.Read(buff)
    str := base64.RawURLEncoding.EncodeToString(buff)
    return str[:l] // strip 1 extra character we get from odd length results
}

注意:如果您喜欢-和_

,则还可以使用RawStdEncoding。

如果要使用十六进制,则基数16的长度比基数256的长度长2倍。(2 ^ 8与2 ^ 4; 8位/ 4位= 2倍比率)

import (
    "crypto/rand"
    "encoding/hex"
    "math"
)


func randomBase16String(l int) string {
    buff := make([]byte, int(math.Round(float64(l)/2)))
    rand.Read(buff)
    str := hex.EncodeToString(buff)
    return str[:l] // strip 1 extra character we get from odd length results
}

但是,如果您的字符集具有从base256到baseN的编码器,则可以将其扩展到任意字符集。您可以使用相同的大小进行计算,以表示字符集需要多少位。任何任意字符集的比率计算为:ratio = 8 / log2(len(charset)))。

尽管这两种解决方案都是安全,简单,应该快速的,而且不会浪费您的加密熵池。

这里是操场,显示它可以使用任何尺寸。 https://play.golang.org/p/i61WUVR8_3Z

答案 11 :(得分:0)

这是我的方式)根据需要使用数学rand或crypto rand。

func randStr(len int) string {
    buff := make([]byte, len)
    rand.Read(buff)
    str := base64.StdEncoding.EncodeToString(buff)
    // Base 64 can be longer than len
    return str[:len]
}

答案 12 :(得分:-1)

/*
    korzhao
*/

package rand

import (
    crand "crypto/rand"
    "math/rand"
    "sync"
    "time"
    "unsafe"
)

// Doesn't share the rand library globally, reducing lock contention
type Rand struct {
    Seed int64
    Pool *sync.Pool
}

var (
    MRand    = NewRand()
    randlist = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
)

// init random number generator
func NewRand() *Rand {
    p := &sync.Pool{New: func() interface{} {
        return rand.New(rand.NewSource(getSeed()))
    },
    }
    mrand := &Rand{
        Pool: p,
    }
    return mrand
}

// get the seed
func getSeed() int64 {
    return time.Now().UnixNano()
}

func (s *Rand) getrand() *rand.Rand {
    return s.Pool.Get().(*rand.Rand)
}
func (s *Rand) putrand(r *rand.Rand) {
    s.Pool.Put(r)
}

// get a random number
func (s *Rand) Intn(n int) int {
    r := s.getrand()
    defer s.putrand(r)

    return r.Intn(n)
}

//  bulk get random numbers
func (s *Rand) Read(p []byte) (int, error) {
    r := s.getrand()
    defer s.putrand(r)

    return r.Read(p)
}

func CreateRandomString(len int) string {
    b := make([]byte, len)
    _, err := MRand.Read(b)
    if err != nil {
        return ""
    }
    for i := 0; i < len; i++ {
        b[i] = randlist[b[i]%(62)]
    }
    return *(*string)(unsafe.Pointer(&b))
}

24.0 ns / op 16 B / op 1分配/

答案 13 :(得分:-1)

const (
    chars       = "0123456789_abcdefghijkl-mnopqrstuvwxyz" //ABCDEFGHIJKLMNOPQRSTUVWXYZ
    charsLen    = len(chars)
    mask        = 1<<6 - 1
)

var rng = rand.NewSource(time.Now().UnixNano())

// RandStr 返回指定长度的随机字符串
func RandStr(ln int) string {
    /* chars 38个字符
     * rng.Int63() 每次产出64bit的随机数,每次我们使用6bit(2^6=64) 可以使用10次
     */
    buf := make([]byte, ln)
    for idx, cache, remain := ln-1, rng.Int63(), 10; idx >= 0; {
        if remain == 0 {
            cache, remain = rng.Int63(), 10
        }
        buf[idx] = chars[int(cache&mask)%charsLen]
        cache >>= 6
        remain--
        idx--
    }
    return *(*string)(unsafe.Pointer(&buf))
}

BenchmarkRandStr16-8 20000000 68.1 ns / op 16 B / op 1 allocs / op

答案 14 :(得分:-1)

如何随机计数:

count, one := big.NewInt(0), big.NewInt(1)
count.SetString("100000000000000000000000", 10)

答案 15 :(得分:-1)

如果您愿意在允许的字符池中添加几个字符,则可以使代码适用于通过io.Reader提供随机字节的任何内容。我们在这里使用crypto/rand

// len(encodeURL) == 64. This allows (x <= 265) x % 64 to have an even
// distribution.
const encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"

// A helper function create and fill a slice of length n with characters from
// a-zA-Z0-9_-. It panics if there are any problems getting random bytes.
func RandAsciiBytes(n int) []byte {
    output := make([]byte, n)

    // We will take n bytes, one byte for each character of output.
    randomness := make([]byte, n)

    // read all random
    _, err := rand.Read(randomness)
    if err != nil {
        panic(err)
    }

    // fill output
    for pos := range output {
        // get random item
        random := uint8(randomness[pos])

        // random % 64
        randomPos := random % uint8(len(encodeURL))

        // put into output
        output[pos] = encodeURL[randomPos]
    }

    return output
}

答案 16 :(得分:-1)

此外,我发现了一个包含大量方法来处理虚假数据的包。发现它在开发https://github.com/Pallinder/go-randomdata时播种数据库很有用。也可能对其他人有帮助