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?
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))
}
Deux options possibles (il y en a peut-être plus, bien sûr):
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.
Prenez un nombre aléatoire et hachez-le avec md5 ou quelque chose comme ça.
Utilisez le package uniuri , qui génère des chaînes uniformes (non biaisées) sécurisées de manière cryptographique.
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
.
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]
}
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
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