像 [a-zA-Z0-9] 字符串:
na1dopW129T0anN28udaZ
或十六进制字符串:
8c6f78ac23b4a7b8c0182d
长我的意思是2K和更多的字符。
这在我的盒子上大约 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,您可以在任何需要的地方重复使用它来生成随机流。
您可以使用 Go 包uniuri生成随机字符串(或查看源代码以了解他们是如何做到的)。你会想要使用:
func NewLen(length int) string
NewLen 返回一个由标准字符组成的具有所提供长度的新随机字符串。
或者,指定使用的字符集:
func NewLenChars(length int, chars []byte) string
这实际上有点偏向于集合中的前 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)
}
在这里,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)
}
如果你想生成加密安全的随机字符串,我建议你看看这个页面。这是一个辅助函数,它从操作系统的随机源中读取n
随机字节,然后使用这些字节对其进行 base64 编码。请注意,字符串长度会比n
base64 大。
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)
}