Generación de cadenas y números aleatorios en Go

Mientrascompletamente al azar no es realmente posible, todavía podemos tener números pseudoaleatorios en las computadoras.

Podemos tenerregularnúmeros pseudoaleatorios, ycriprográficamente seguronúmeros pseudoaleatorios.

Veamos cómo hacerlo en Go.

Números pseudoaleatorios

losmath/randpaqueteproporcionada por Go Standard Library nos brindageneradores de números pseudoaleatorios (PRNG), también llamadogeneradores de bits aleatorios deterministas.

Al igual que con todos los generadores de pseudo números, cualquier número generado a través demath/randesno es realmente aleatoriopor defecto, como deterministasiempre imprimirá el mismo valor cada vez.

Como ejemplo, intente ejecutar este código que introducerand.Intn(n), que devuelve un número aleatorio entre0yn - 1.

package main

import ( “fmt” “math/rand” )

func main() { fmt.Println(rand.Intn(30)) fmt.Println(rand.Intn(30)) fmt.Println(rand.Intn(30)) fmt.Println(rand.Intn(30)) fmt.Println(rand.Intn(30)) }

patio de recreo

Siempre verá la misma secuencia cada vez que ejecute el programa. El número aleatorio cambia dentro del programa, pero cada vez que lo ejecuta, obtendrá el mismo resultado:

11
27
17
29
1

Esto se debe a que, por defecto, la semilla es siempre la misma, el número1. Para obtener un número aleatorio, debe proporcionar un únicosemillapara su programa. Realmente desea no olvidar la siembra y, en su lugar, siembre correctamente nuestro generador de pseudonumber. ¿Cómo?

Usarrand.Seed()antes de llamar a cualquiermath/randmétodo, pasando unint64valor. Solo necesita sembrar una vez en su programa, no cada vez que necesite un número aleatorio. La semilla más utilizada es la hora actual, convertida aint64porUnixNanoconrand.Seed(time.Now().UnixNano()):

package main

import ( “fmt” “math/rand” “time” )

func main() { rand.Seed(time.Now().UnixNano()) fmt.Println(rand.Intn(30)) fmt.Println(rand.Intn(30)) fmt.Println(rand.Intn(30)) fmt.Println(rand.Intn(30)) fmt.Println(rand.Intn(30)) }

Recuerde que debido a su entorno de pruebas, Go Playground siempre comienza con la misma hora, por lo que este código no funcionará como se esperaba. Pruébelo en un entorno real, y los números que antes no cambiaban, ahora se imprimirán de manera diferente cada vez que ejecute el programa.

A continuación se enumeran algunos ejemplos comunes para facilitar su reutilización:

Genera un entero aleatorio

package main

import ( “fmt” “math/rand” “time” )

// Returns an int >= min, < max func randomInt(min, max int) int { return min + rand.Intn(max-min) }

func main() { rand.Seed(time.Now().UnixNano()) fmt.Println(randomInt(1, 11)) //get an int in the 1…10 range }

Genera una cadena aleatoria

package main

import ( “fmt” “math/rand” “time” )

// Returns an int >= min, < max func randomInt(min, max int) int { return min + rand.Intn(max-min) }

// Generate a random string of A-Z chars with len = l func randomString(len int) string { bytes := make([]byte, len) for i := 0; i < len; i++ { bytes[i] = byte(randomInt(65, 90)) } return string(bytes) }

func main() { rand.Seed(time.Now().UnixNano()) fmt.Println(randomString(10)) // print 10 chars }

Devolverá 10 caracteres en mayúsculas. Cambio

bytes[i] = byte(randomInt(65, 90))

a

bytes[i] = byte(randomInt(97, 122))

solo por minúsculas.

Si, en cambio, desea tener un grupo de caracteres específicos para elegir, use

package main

import ( “fmt” “math/rand” “time” )

var pool = “_:$%&/()”

// Generate a random string of A-Z chars with len = l func randomString(l int) string { bytes := make([]byte, l) for i := 0; i < l; i++ { bytes[i] = pool[rand.Intn(len(pool))] } return string(bytes) }

func main() { rand.Seed(time.Now().UnixNano()) fmt.Println(randomString(1000)) }

Cambiolen(pool)autf8.RuneCountInString(pool)si usa cadenas no ascii, comolen()cuenta los bytes, pero no todos los caracteres toman solo un byte en Unicode - veahttps://stackoverflow.com/questions/12668681/how-to-get-the-number-of-characters-in-a-string.

Genera una matriz aleatoria de enteros

package main

import ( “fmt” “math/rand” “time” )

func randomArray(len int) []int { a := make([]int, len) for i := 0; i <= len-1; i++ { a[i] = rand.Intn(len) } return a }

func main() { rand.Seed(time.Now().UnixNano()) fmt.Println(randomArray(10)) }

Números aleatorios a nivel criptográfico

Go también proporciona unGenerador de números pseudoaleatorios criptográficamente seguro (CSPRNG)en el paquete de biblioteca estándarcrypto.rand

Entonces, podría preguntarse, ¿por qué debería usar la biblioteca del generador aleatorio de pseudo-números proporcionada pormath/rand¿en lugar de? Bueno, depende del caso de uso.math/randesmucho mas rápidopara aplicaciones que no necesitan generación de datos aleatorios de nivel criptográfico o relacionados con la seguridad.crypto.randes adecuado para un uso seguro y listo para criptografía, pero es más lento.

¿Para qué debe usarse? Por ejemplo, generar contraseñas, tokens CSRF, claves de sesión o cualquier cosa relacionada de forma remota con la seguridad.

No depende de la hora actual, como hicimos en los ejemplos anteriores enmath/rand, pero en su lugar utiliza las API CSPRNG del sistema operativo: la API CryptGenRandom en Windows, y/dev/urandom/en todos los demás (Linux, OSX, * nix)

Obtienes 256 bytes aleatorios directamente con

package main

import ( “crypto/rand” “fmt” )

func main() { key := [256]byte{} _, err := rand.Read(key[:]) if err != nil { panic(err) } fmt.Println(key) }

Estoy tomando una muestra de código deMatt Silverlock: puede hacerlo más general y crear una función de generación de bytes aleatorios

// GenerateRandomBytes returns securely generated random bytes.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomBytes(n int) ([]byte, error) {
    b := make([]byte, n)
    _, err := rand.Read(b)
    // Note that err == nil only if we read len(b) bytes.
    if err != nil {
        return nil, err
    }
<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">b</span>, <span style="color:#66d9ef">nil</span>

}

y usando esto, una función de generación de cadenas aleatorias,

// GenerateRandomString returns a URL-safe, base64 encoded
// securely generated random string.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomString(s int) (string, error) {
    b, err := GenerateRandomBytes(s)
    return base64.URLEncoding.EncodeToString(b), err
}

Más tutoriales de go: