15

像 [a-zA-Z0-9] 字符串:

na1dopW129T0anN28udaZ

或十六进制字符串:

8c6f78ac23b4a7b8c0182d

长我的意思是2K和更多的字符。

4

5 回答 5

23

这在我的盒子上大约 200MBps。有明显的改进空间。

type randomDataMaker struct {
    src rand.Source
}

func (r *randomDataMaker) Read(p []byte) (n int, err error) {
    for i := range p {
        p[i] = byte(r.src.Int63() & 0xff)
    }
    return len(p), nil
}

你只需要io.CopyN用来产生你想要的字符串。显然,您可以在途中或其他任何地方调整字符集。

这个模型的好处是它只是一个,io.Reader所以你可以用它来做任何事情。

测试如下:

func BenchmarkRandomDataMaker(b *testing.B) {
    randomSrc := randomDataMaker{rand.NewSource(1028890720402726901)}
    for i := 0; i < b.N; i++ {
        b.SetBytes(int64(i))
        _, err := io.CopyN(ioutil.Discard, &randomSrc, int64(i))
        if err != nil {
            b.Fatalf("Error copying at %v: %v", i, err)
        }
    }
}

在我的 2.2GHz i7 的一个核心上:

BenchmarkRandomDataMaker       50000        246512 ns/op     202.83 MB/s

编辑

自从我编写了基准,我想我会做明显的改进(不那么频繁地调用随机数)。使用 1/8 的 rand 调用,它的运行速度提高了大约 4 倍,尽管它更丑:

新版本:

func (r *randomDataMaker) Read(p []byte) (n int, err error) {
    todo := len(p)
    offset := 0
    for {
        val := int64(r.src.Int63())
        for i := 0; i < 8; i++ {
            p[offset] = byte(val & 0xff)
            todo--
            if todo == 0 {
                return len(p), nil
            }
            offset++
            val >>= 8
        }
    }

    panic("unreachable")
}

新基准:

BenchmarkRandomDataMaker      200000        251148 ns/op     796.34 MB/s

编辑 2

因为它是多余的,所以去掉了转换为字节的掩码。更快地获得一笔好交易:

BenchmarkRandomDataMaker      200000        231843 ns/op     862.64 MB/s

(这比真正的工作要容易得多

编辑 3

这出现在 irc 今天,所以我发布了一个库。此外,我的实际基准测试工具虽然对相对速度有用,但其报告不够准确。

我创建了randbo,您可以在任何需要的地方重复使用它来生成随机流。

于 2012-10-10T00:26:46.503 回答
22

您可以使用 Go 包uniuri生成随机字符串(或查看源代码以了解他们是如何做到的)。你会想要使用:

func NewLen(length int) string

NewLen 返回一个由标准字符组成的具有所提供长度的新随机字符串。

或者,指定使用的字符集:

func NewLenChars(length int, chars []byte) string
于 2012-10-07T20:43:55.160 回答
16

这实际上有点偏向于集合中的前 8 个字符(因为 255 不是 的倍数len(alphanum)),但这会让你大部分时间到达那里。

import (
    "crypto/rand"
)

func randString(n int) string {
    const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    var bytes = make([]byte, n)
    rand.Read(bytes)
    for i, b := range bytes {
        bytes[i] = alphanum[b % byte(len(alphanum))]
    }
    return string(bytes)
}
于 2012-10-07T20:37:38.890 回答
1

在这里,Evan Shaw 的答案重新工作,没有偏向字符串的前 8 个字符。请注意,它使用了许多昂贵的big.Int操作,所以可能不是那么快!答案是加密货币强。

它用于rand.Int生成一个大小正好合适的整数len(alphanum) ** n,然后进行有效的 base 转换为 base len(alphanum)

几乎可以肯定有一个更好的算法来解决这个问题,它需要保留一个小得多的余数,并在必要时向其中添加随机字节。这将摆脱昂贵的长整数运算。

import (
    "crypto/rand"
    "fmt"
    "math/big"
)

func randString(n int) string {
    const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    symbols := big.NewInt(int64(len(alphanum)))
    states := big.NewInt(0)
    states.Exp(symbols, big.NewInt(int64(n)), nil)
    r, err := rand.Int(rand.Reader, states)
    if err != nil {
        panic(err)
    }
    var bytes = make([]byte, n)
    r2 := big.NewInt(0)
    symbol := big.NewInt(0)
    for i := range bytes {
        r2.DivMod(r, symbols, symbol)
        r, r2 = r2, r
        bytes[i] = alphanum[symbol.Int64()]
    }
    return string(bytes)
}
于 2012-10-09T07:57:47.850 回答
1

如果你想生成加密安全的随机字符串,我建议你看看这个页面。这是一个辅助函数,它从操作系统的随机源中读取n随机字节,然后使用这些字节对其进行 base64 编码。请注意,字符串长度会比nbase64 大。

package main

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

func GenerateRandomBytes(n int) ([]byte, error) {
    b := make([]byte, n)
    _, err := rand.Read(b)
    if err != nil {
        return nil, err
    }

    return b, nil
}

func GenerateRandomString(s int) (string, error) {
    b, err := GenerateRandomBytes(s)
    return base64.URLEncoding.EncodeToString(b), err
}

func main() {
    token, _ := GenerateRandomString(32)
    fmt.Println(token)
}
于 2015-11-19T02:43:29.743 回答