Goでは、ランダムな文字列(大文字または小文字)のみを使用し、数字は使用しないでください。これを行うための最速かつ最も簡単な方法は何ですか?
あなたはそれのためのコードを書くことができます。このコードは、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))
}
考えられる選択肢は2つあります(もちろんもっとあります)。
(/ dev/urandomからの)ランダムバイト配列の読み取りをサポートし、暗号化ランダム生成を対象としたcrypto/Rand
パッケージを使用できます。 http://golang.org/pkg/crypto/Rand/#example_Read を参照してください。ただし、通常の擬似乱数生成よりも遅くなる可能性があります。
乱数を取り、md5またはこれに似たものを使ってハッシュします。
暗号的に安全で統一された(バイアスのない)文字列を生成するpackage uniuri を使用してください。
免責事項:私はパッケージの作者です
icza's
が素晴らしく説明された解決策に続いて、これはcrypto/Rand
の代わりに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
}
より一般的な解決策が必要な場合は、文字バイトのスライスを渡して文字列を作成することができます。これを使用して試すことができます。
// 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)
}
独自の乱数の発生源を渡したい場合は、io.Reader
を使用するのではなく、crypto/Rand
を受け入れるように上記を変更することが簡単です。
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割り当て/ op
暗号学的に安全な乱数が必要で、正確な文字セットが柔軟である場合(base64が問題ないなど)、必要な乱数の長さを正確に計算できます。希望する出力サイズ.
基数64のテキストは、基数256よりも1/3長くなります。(2 ^ 8対2 ^ 6、8ビット/ 6ビット= 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
}
注:+と/の文字を - と_よりも使用したい場合は、RawStdEncodingも使用できます。
16進数が必要な場合は、基数16の方が基数256よりも2倍長くなります(2 ^ 8対2 ^ 4、8ビット/ 4ビット= 2倍の比率)。
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
}
ただし、文字セットにbase256からbaseNへのエンコーダがある場合は、これを任意の文字セットに拡張できます。文字セットを表すのに必要なビット数を使用して、同じサイズの計算を実行できます。任意の文字セットに対する比率の計算は、ratio = 8 / log2(len(charset))
)です。
これらの解決策はどちらも安全で単純で、高速でなければならず、暗号エントロピープールを無駄にしないでください。
これは、それがあらゆるサイズで機能することを示す遊び場です。 https://play.golang.org/p/i61WUVR8_3Z
これが私のやり方です)あなたが望むように数学のRandか暗号のRandを使ってください。
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]
}