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


当前回答

这里是一个简单而高效的加密安全随机字符串的解决方案。

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] % byte(len(alphabet))]
    }
    return *(*string)(unsafe.Pointer(&b))
}

基准

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

其他回答

两个可能的选项(当然可能有更多):

您可以使用crypto/rand包,它支持读取随机字节数组(来自/dev/urandom),并面向加密随机生成。见http://golang.org/pkg/crypto/rand/#example_Read。它可能比普通的伪随机数生成慢。 取一个随机数,用md5或类似的方法进行哈希。

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

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
}

作为icza的出色解决方案的后续,下面我使用rand。读者

func RandStringBytesMaskImprRandReaderUnsafe(length uint) (string, error) {
    const (
        charset     = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        charIdxBits = 6                  // 6 bits to represent a letter index
        charIdxMask = 1<<charIdxBits - 1 // All 1-bits, as many as charIdxBits
        charIdxMax  = 63 / charIdxBits   // # of letter indices fitting in 63 bits
    )

    buffer := make([]byte, length)
    charsetLength := len(charset)
    max := big.NewInt(int64(1 << uint64(charsetLength)))

    limit, err := rand.Int(rand.Reader, max)
    if err != nil {
        return "", err
    }

    for index, cache, remain := int(length-1), limit.Int64(), charIdxMax; index >= 0; {
        if remain == 0 {
            limit, err = rand.Int(rand.Reader, max)
            if err != nil {
                return "", err
            }

            cache, remain = limit.Int64(), charIdxMax
        }

        if idx := int(cache & charIdxMask); idx < charsetLength {
            buffer[index] = charset[idx]
            index--
        }

        cache >>= charIdxBits
        remain--
    }

    return *(*string)(unsafe.Pointer(&buffer)), nil
}


func BenchmarkBytesMaskImprRandReaderUnsafe(b *testing.B) {
    b.ReportAllocs()
    b.ResetTimer()

    const length = 16

    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            RandStringBytesMaskImprRandReaderUnsafe(length)
        }
    })
}

你可以为它编写代码。如果您希望使用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))
}

如果您想要加密安全的随机数,并且确切的字符集是灵活的(例如,base64就可以),那么您可以根据所需的输出大小精确计算所需的随机字符的长度。

64进制文本比256进制文本长1/3。(2^8 vs 2^6;8位/6位= 1.333比率)

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

func randomBase64String(l int) string {
    buff := make([]byte, int(math.Ceil(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 vs 2^4;8位/4位= 2x比例)

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


func randomBase16String(l int) string {
    buff := make([]byte, int(math.Ceil(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(字符集)))。

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

这是一个游乐场,它适用于任何尺寸。https://play.golang.org/p/_yF_xxXer0Z