儘管完全隨機是不可能的,我們仍然可以在計算機上使用偽隨機數。
我們可以有常規的偽隨機數,以及圖像安全偽隨機數。
讓我們看看如何在Go中做到這一點。
偽隨機數
這math/rand
包裹Go標準庫提供的內容使我們偽隨機數生成器(PRNG), 也被稱為確定性隨機位發生器。
與所有偽數字生成器一樣,通過math/rand
是不是很隨機默認情況下是確定性的每次都會打印相同的值。
例如,嘗試運行此代碼,其中介紹了rand.Intn(n)
,返回介於之間的隨機數0
和n - 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))
}
每次運行該程序時,您總是會看到相同的順序。隨機數在程序內部會發生變化,但是每次運行它時,您都會得到相同的輸出:
11
27
17
29
1
這是因為默認情況下,種子始終是相同的,即數字1
。要實際獲得一個隨機數,您需要提供一個唯一的種子為您的程序。您確實不希望忘記播種,而是正確地為我們的偽數生成器播種。如何?
使用rand.Seed()
在打電話給任何人之前math/rand
方法,通過int64
價值。您只需要在程序中播種一次,而不是每次都需要一個隨機數。最常用的種子是當前時間,轉換為int64
經過Unix納米和rand.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))
}
請記住,由於其沙箱操作,Go Playground總是在同一時間開始,因此此代碼無法按預期工作。在實際環境中進行嘗試,並且以前沒有改變的數字現在每次運行該程序時,它們的打印方式都會有所不同。
下面列出了一些常見示例,以方便重用:
產生一個隨機整數
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
}
產生隨機字串
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
}
將以大寫格式返回10個字符。改變
bytes[i] = byte(randomInt(65, 90))
到
bytes[i] = byte(randomInt(97, 122))
僅小寫。
如果您想從中選擇特定的字符池,請使用
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))
}
改變len(pool)
到utf8.RuneCountInString(pool)
如果您使用非ascii字符串,則為len()
計算字節數,但並非所有字符都只佔用Unicode中的一個字節-請參見https://stackoverflow.com/questions/12668681/how-to-get-the-number-of-characters-in-a-string。
生成隨機整數數組
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))
}
加密級隨機數
去還提供了加密安全的偽隨機數生成器(CSPRNG)在標準庫包中crypto.rand
所以您可能會問,為什麼我還要使用由提供的偽數隨機生成器庫?math/rand
反而?好吧,這取決於用例。math/rand
是快多了適用於不需要加密級別或與安全性相關的隨機數據生成的應用程序。crypto.rand
適用於安全且可加密的用途,但速度較慢。
它應該用於什麼?例如,生成密碼,CSRF令牌,會話密鑰或與安全性遠程相關的任何內容。
它不依賴於當前時間,就像我們在前面的示例中所做的那樣math/rand
,而是使用操作系統CSPRNG API:Windows上的CryptGenRandom API和/dev/urandom/
在所有其他版本(Linux,OSX,* nix)上
您直接獲得256個隨機字節
package main
import (
“crypto/rand”
“fmt”
)
func main() {
key := [256]byte{}
_, err := rand.Read(key[:])
if err != nil {
panic(err)
}
fmt.Println(key)
}
我正在從中獲取代碼示例馬特·西爾弗洛克(Matt Silverlock):您可以使其更通用,並創建一個隨機字節生成函數
// 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>
}
並使用它,一個隨機字符串生成函數,
// 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
}
更多教程:
- 使用NGINX反向代理服務Go服務
- 在Go中復制結構
- Go Web服務器的基礎
- 在Go中對地圖類型進行排序
- 簡而言之去指針
- 轉到標籤說明
- 開始日期和時間格式
- 使用Go進行JSON處理
- 可變參數函數
- 去弦備忘單
- 轉到空界面說明
- 使用VS Code和Delve調試Go
- 命名為Go返回參數
- 在Go中生成隨機數和字符串
- Go項目的文件系統結構
- Go中的二進制搜索算法
- 在Go中使用命令行標誌
- GOPATH解釋
- 使用Go構建一個命令行應用程序:lolcat
- 使用Go構建CLI命令:Cowsay
- 在Go中使用殼管
- Go CLI教程:財富克隆
- 使用Go列出文件夾中的文件
- 使用Go從GitHub獲取存儲庫列表
- 去,將一小段字符串附加到文件中
- 去,將字符串轉換為字節片
- 使用Go可視化您本地的Git貢獻
- Go CPU和內存分析入門
- 解決Go程序中的“不支持索引”錯誤
- 測量Go程序中的執行時間
- 使用Go構建Web爬網程序以檢測重複的標題
- 最佳實踐:指針還是價值接收者?
- 最佳實踐:您應該使用方法還是函數?
- Go數據結構:集
- 前往地圖備忘單
- 在Go中生成泛型類型的實現
- Go數據結構:字典
- Go數據結構:哈希表
- 在“通過通道”中實現事件偵聽器
- Go數據結構:堆棧
- Go數據結構:隊列
- Go數據結構:二進制搜索樹
- Go數據結構:圖形
- Go數據結構:鍊錶
- Go數據結構的完整指南
- 比較Go值
- Go是面向對象的嗎?
- 在Go中使用SQL數據庫
- 在Go中使用環境變量
- 上篇教程:PostgreSQL支持的REST API
- 在Go Web服務器上啟用CORS
- 在Docker容器中部署Go應用程序
- 為什麼Go是作為PHP開發人員學習的功能強大的語言
- 去,刪除io.Reader.ReadString換行符
- 開始,如何觀看更改並重建程序
- 去算一下自約會以來的月份
- 在Go中訪問HTTP POST參數