在Golang中生成固定长度的随机十六进制字符串的有效方法? [英] Efficient way to to generate a random Hex string of a fixed length in Golang?
问题描述
我需要生成许多固定长度的随机十六进制字符串. 我找到了这种解决方案如何在golang中生成固定长度的随机字符串?
I need to generate a lot of a random hex string of a fixed length. I find this solution How to generate a random string of a fixed length in golang?
我正在做这样的事情:
const letterBytes = "abcdef0123456789"
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
)
var src = rand.NewSource(time.Now().UnixNano())
// RandStringBytesMaskImprSrc ...
// Src: https://stackoverflow.com/a/31832326/710955
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)
}
var tryArr = make([]string, 10000)
for i := 0; i < 10000; i++ {
tryArr[i] = RandStringBytesMaskImprSrc(8)
}
但是我遇到了这个紧急错误
But I got this panic error
panic: runtime error: index out of range
goroutine 36 [running]:
math/rand.(*rngSource).Int63(0x11bb1300, 0x8, 0x8)
D:/Applications/Go/src/math/rand/rng.go:231 +0xa0
main.RandStringBytesMaskImprSrc(0x8, 0x11f81be8, 0x8)
main.go:60 +0x5f
错误似乎在for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0;
中,但我不知道为什么会出现此错误.
The errror seem to be in for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0;
, but I don't find why there is this error.
在Go中生成大量固定长度的随机十六进制字符串的最快,最简单的方法是什么?
What is the fastest and simplest way to generate a lot of a random hex string of a fixed length in Go?
基准
package bench
import (
"encoding/hex"
"math/rand"
"testing"
"time"
)
const letterBytes = "abcdef0123456789"
const (
letterIdxBits = 4 // 4 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
)
var src1 = rand.NewSource(time.Now().UnixNano())
var src2 = rand.New(rand.NewSource(time.Now().UnixNano()))
// RandStringBytesMaskImprSrc returns a random hexadecimal string of length n.
func RandStringBytesMaskImprSrc1(n int) string {
b := make([]byte, n)
for i, cache, remain := n-1, src1.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src1.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}
func RandStringBytesMaskImprSrc2(n int) string {
b := make([]byte, (n+1)/2) // can be simplified to n/2 if n is always even
if _, err := src2.Read(b); err != nil {
panic(err)
}
return hex.EncodeToString(b)[:n]
}
func BenchmarkRandStringBytesMaskImprSrc1(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = RandStringBytesMaskImprSrc1(8)
}
}
func BenchmarkRandStringBytesMaskImprSrc2(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = RandStringBytesMaskImprSrc2(8)
}
}
goos: windows
goarch: 386
BenchmarkRandStringBytesMaskImprSrc1-4 20000000 116 ns/op 16 B/op 2 allocs/op
BenchmarkRandStringBytesMaskImprSrc2-4 10000000 231 ns/op 24 B/op 3 allocs/op
PASS
ok command-line-arguments 5.139s
=> icza RandStringBytesMaskImprSrc
解决方案更有效
=> icza RandStringBytesMaskImprSrc
solution is more efficient
推荐答案
实际上,您发布的代码可以运行,即使其中有错误(请参见下文),它也不会引起恐慌(只会使性能变差) ).
Actually the code you posted runs, as even though there's a mistake in it (see below), it still doesn't cause a panic (just makes performance worse).
您发布的堆栈跟踪指示math/rand
软件包中的错误,我没有遇到过.请发布完整代码和Go版本+ env(go version
和go env
).
The stack trace you posted indicates error in the math/rand
package, I did not experience it. Please post full code and Go version + env (go version
and go env
).
事实证明,询问者正在同时从多个goroutines调用RandStringBytesMaskImprSrc()
. RandStringBytesMaskImprSrc()
使用共享的rand.Source
实例,该实例对于并发使用是不安全的,因此math/rand
程序包会出现恐慌.解决方法是为每个goroutine创建一个单独的rand.Source()
,并将其传递给RandStringBytesMaskImprSrc()
.
As it turns out, the asker was calling RandStringBytesMaskImprSrc()
concurrently, from multiple goroutines. RandStringBytesMaskImprSrc()
uses a shared rand.Source
instance which is not safe for concurrent use, hence the panic from the math/rand
package. Fix is to create a separate rand.Source()
for each goroutines, and pass that to RandStringBytesMaskImprSrc()
.
在配置"中有一个错误.开头的常量:
There is a mistake in the "configuration" constants at the beginning:
const letterBytes = "abcdef0123456789"
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
)
常数letterIdxBits
应包含表示符号索引所需的位数.由于您使用的字母是16个元素(长度为letterBytes
),因此16个组合仅需要4位:
The constant letterIdxBits
should contain how many bits are required to represent a symbol index. Since you're using an alphabet of 16 elements (the length of letterBytes
), 16 combinations require only 4 bits:
letterIdxBits = 4 // 4 bits to represent a letter index
测试示例:
var tryArr = make([]string, 10)
for i := range tryArr {
tryArr[i] = RandStringBytesMaskImprSrc(8)
}
fmt.Println(tryArr)
输出(在游乐场上尝试):
[d3e7caa6 a69c9b7d c37a613b 92d5a43b 64059c4a 4f08141b 70130c65 1546daaf fe140fcd 0d714e4d]
(注意:由于Go游乐场的开始时间是固定的,并且输出已缓存,您将始终看到这些随机生成的字符串.在计算机上运行以查看随机结果.)
(Note: since the starting time on the Go playground is fixed and output is cached, you will always see these random generated strings. Run it on your machine to see random results.)
这篇关于在Golang中生成固定长度的随机十六进制字符串的有效方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!