web-dev-qa-db-fra.com

Comment générer une chaîne aléatoire d'une longueur fixe dans Go?

Je veux une chaîne aléatoire de caractères uniquement (majuscules ou minuscules), pas de chiffres, dans Go. Quel est le moyen le plus simple et le plus rapide de procéder?

218
Anish Shah

Vous pouvez simplement écrire du code pour cela. Ce code peut être un peu plus simple si vous voulez que les lettres soient des octets simples lorsqu'elles sont codées en 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))
}
99
Paul Hankin

Deux options possibles (il y en a peut-être plus, bien sûr):

  1. Vous pouvez utiliser le package crypto/Rand qui prend en charge la lecture de tableaux d’octets aléatoires (à partir de/dev/urandom) et qui est conçu pour la génération aléatoire cryptographique. voir http://golang.org/pkg/crypto/Rand/#example_Read . Cela pourrait cependant être plus lent que la génération normale de nombres pseudo-aléatoires. 

  2. Prenez un nombre aléatoire et hachez-le avec md5 ou quelque chose comme ça.

15
Not_a_Golfer

Utilisez le package uniuri , qui génère des chaînes uniformes (non biaisées) sécurisées de manière cryptographique.

9
dchest

Après la solution merveilleusement expliquée par icza's, voici une modification qui utilise crypto/Rand au lieu de 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
}

Si vous voulez une solution plus générique, qui vous permette de passer la tranche d'octets de caractères pour créer la chaîne, vous pouvez utiliser ceci:

// 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)
}

Si vous souhaitez transmettre votre propre source d’aléatoire, il serait trivial de modifier ce qui précède pour accepter un io.Reader au lieu d’utiliser crypto/Rand.

3
Chris

Voici mon chemin) Utilisez math Rand ou crypto Rand comme vous le souhaitez.

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]
}
1
Dima
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

0
user10987909

Si vous voulez cryptographiquement sécurisé nombres aléatoires, et que le jeu de caractères exact soit flexible (par exemple, base64 convient), vous pouvez calculer exactement la longueur de caractères aléatoires dont vous avez besoin à partir de la taille de sortie souhaitée.

Le texte de la base 64 est 1/3 plus long que la base 256. (2 ^ 8 vs 2 ^ 6; 8 bits/6 bits = rapport 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
}

Remarque: vous pouvez également utiliser RawStdEncoding si vous préférez les caractères + et/pour - et _.

Si vous voulez l'hexagone, la base 16 est 2x plus longue que la base 256. (2 ^ 8 vs 2 ^ 4; 8bits/4bits = rapport 2x)

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
}

Toutefois, vous pouvez l'étendre à tout jeu de caractères arbitraire si vous disposez d'un encodeur base256 à baseN pour votre jeu de caractères. Vous pouvez faire le même calcul de taille avec le nombre de bits nécessaires pour représenter votre jeu de caractères. Le calcul du ratio pour tout jeu de caractères arbitraire est: ratio = 8 / log2(len(charset))).

Bien que ces deux solutions soient sécurisées et simples, elles devraient être rapides et ne gaspillez pas votre pool d'entropie crypto.

Voici le terrain de jeu montrant qu'il fonctionne pour n'importe quelle taille. https://play.golang.org/p/i61WUVR8_3Z

0
Steven Soroka