바둑에서 고정된 길이의 임의의 문자열을 생성하는 방법은 무엇입니까?
Go에서 숫자 없이 문자(대문자 또는 소문자)만 임의 문자열을 원합니다.가장 빠르고 간단한 방법은 무엇입니까?
Paul의 솔루션은 간단하고 일반적인 솔루션을 제공합니다.
이 질문은 "가장 빠르고 간단한 방법"을 묻는 것입니다.가장 빠른 부분도 말씀드리겠습니다.우리는 반복적인 방식으로 최종적이고 가장 빠른 코드에 도달할 수 있습니다.각 반복을 벤치마킹하는 방법은 답변의 끝에서 확인할 수 있습니다.
모든 솔루션과 벤치마킹 코드는 Go Playground에서 확인할 수 있습니다.Playground의 코드는 실행 파일이 아닌 테스트 파일입니다.이름이 지정된 파일에 저장해야 합니다.XX_test.go
로다니합으로 합니다.
go test -bench . -benchmem
서문:
랜덤 문자열만 필요한 경우 가장 빠른 솔루션은 이동식 솔루션이 아닙니다.그것을 위해, Paul의 해결책은 완벽합니다.성능이 중요한 경우입니다.처음 2단계(바이트 및 나머지)는 허용 가능한 절충안이지만, 성능이 약 50% 향상됩니다(II의 정확한 수치 참조). 벤치마크 섹션)에서는 복잡성이 크게 증가하지 않습니다.
그렇기는 하지만, 가장 빠른 해결책이 필요하지 않더라도, 이 답을 읽어보는 것은 모험적이고 교육적일 수 있습니다.
I. 개선 사항
제네시스(런)
다시 한 번 말씀드리지만, 저희가 개선하고 있는 원래의 일반적인 솔루션은 다음과 같습니다.
func init() {
rand.Seed(time.Now().UnixNano())
}
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func RandStringRunes(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
바이트
임의의 문자열을 선택하고 조합할 문자가 영어 알파벳의 대문자와 소문자만 포함하는 경우, 영어 알파벳 문자가 UTF-8 인코딩(Go가 문자열을 저장하는 방식)에서 바이트 1대 1로 매핑되기 때문에 바이트로만 작업할 수 있습니다.
그래서 대신에:
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
사용할 수 있습니다.
var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
또는 더 나은 것:
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
입니다. 수 것입니다. 우리는 그것을 성취할 수 있습니다.const
.)string
(슬라이스 상수는 없지만).추가적인 이득으로, 그 표현은len(letters)
또한 다음이 될 것입니다.const
(표현은식은표len(s)
다음과 같은 경우 상수입니다.s
는 문자열 상수입니다.)
그리고 어떤 대가를 치르나요?전혀 없습니다. string
완벽하게 우리가 원하는 대로 바이트를 인덱스하는 인덱스를 만들 수 있습니다.
다음 목적지는 다음과 같습니다.
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
나머지
이전 솔루션에서는 어떤 딜러에게 어떤 딜러를 호출하여 임의의 문자를 지정할 수 있는 임의의 번호를 얻습니다.
이는 63개의 랜덤 비트로 난수를 생성하는 것에 비해 훨씬 느립니다.
그래서 우리는 간단히 전화할 수 있었습니다.rand.Int63()
그리고 나머지를 다음으로 나눈 후에 사용합니다.len(letterBytes)
:
func RandStringBytesRmndr(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))]
}
return string(b)
}
가 있고 더 , 단점은 같지 예: 다음같예빠르며훨, 문것않같다입니다는가정지게정이하확확률이과모자의, 작이동고것단점든은하씬은예uming▁thisass(것다니입가정▁willability▁of▁not▁works▁the▁that▁and:▁the▁is▁the).rand.Int63()
확률이 동일한 모든 63비트 숫자를 생성합니다.,52
▁than다니▁much-▁is작습보다 훨씬 작습니다.1<<63 - 1
그래서 실제로는 이것은 완벽하게 좋습니다.
이를 보다 쉽게 이해하기 위해 다음과 같은 범위에서 임의의 숫자를 원한다고 가정합니다.0..5
비트를 3이 됩니다.0..1
범위에서 보다 두 배의 확률로.2..5
의 랜덤 비트를 하여 5개의 범위 숫자들은 의 랜 사 범 내 용 트 의 숫 자 위 비 덤 개 ▁5 ▁using0..1
것할니다와 함께 할 것입니다.6/32
과 범위의 2..5
와 함께5/32
이제는 원하는 것에 더 가까운 확률.비트 수를 늘리면 63비트에 도달할 때 이는 무시해도 될 정도로 중요하지 않습니다.
마스킹
이전 솔루션을 기반으로, 우리는 글자 수를 나타내는 데 필요한 수의 랜덤 숫자의 가장 낮은 비트만 사용함으로써 동일한 문자 분포를 유지할 수 있습니다.를 들어 글자가 할 수 있습니다: 예를들어개, 비문자있가는, 의경다있표니어우 6시가됩야트다.52 = 110100b
는 그서우리반비환숫 6자트사것다입이 반환한 숫자의 가장 낮은 입니다.rand.Int63()
그리고 문자의 균등한 분포를 유지하기 위해, 우리는 숫자가 범위에 포함되는 경우에만 "수용"합니다.0..len(letterBytes)-1
가장 낮은 비트가 크면 이를 폐기하고 새로운 난수를 쿼리합니다.
이 가낮 은 비 의 가 능 성 다 은 같 다 과 같 습 니 다 음 나 거 장 과 음 트 ▁of ▁to ▁note ▁the ▁to ▁that ▁chance ▁than ▁equal 니 ▁be 다 ▁bits ▁the 가 ▁greater 습 ▁or 장 ▁lowestlen(letterBytes)
보다 .0.5
일반적으로(0.25
평균), 즉, 이 경우가 그렇다 하더라도 이 "잘못된" 경우를 반복하면 좋은 숫자를 찾지 못할 가능성이 줄어듭니다.나 뒤에n
반복, 우리가 여전히 좋은 색인을 가지고 있지 않을 가능성은 훨씬 적습니다.pow(0.5, n)
그리고 이건 단지 추정치가 높습니다., 낮은 않을 은 52자, 6자일 확률은 6자에 불과합니다.(64-52)/64 = 0.19
그 말은 예를 들어 10회 반복 후에 좋은 숫자를 가질 수 없는 가능성이1e-8
.
솔루션은 다음과 같습니다.
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
)
func RandStringBytesMask(n int) string {
b := make([]byte, n)
for i := 0; i < n; {
if idx := int(rand.Int63() & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i++
}
}
return string(b)
}
마스킹 개선
은 이솔루개반은환비된의랜덤 63비트중다사용니 6합트만은낮이 반환한 중 합니다.rand.Int63()
랜덤 비트를 얻는 것이 우리 알고리즘의 가장 느린 부분이기 때문에 이것은 낭비입니다.
52개의 문자가 있다는 것은 6비트가 문자 색인을 암호화한다는 것을 의미합니다.그래서 63개의 무작위 비트가 지정할 수 있습니다.63/6 = 10
여러 문자 색인다음 10개를 모두 사용합니다.
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
func RandStringBytesMaskImpr(n int) string {
b := make([]byte, n)
// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = rand.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}
출처
개선된 마스킹은 꽤 좋습니다. 개선할 수 있는 것은 많지 않습니다.그럴 수는 있지만, 복잡할 가치는 없습니다.
이제 개선할 다른 것을 찾아보겠습니다.난수의 출처입니다.
기능을 제공하는 패키지가 있기 때문에 한 번의 통화로 필요한 만큼의 바이트를 얻을 수 있습니다.이것은 성능 면에서 도움이 되지 않을 것입니다.crypto/rand
암호화 방식으로 안전한 의사 난수 생성기를 구현하므로 속도가 훨씬 느립니다.
자, 그럼 다음과 같이 합시다.math/rand
꾸러미그rand.Rand
는 임의 비트의 소스로 사용합니다. rand.Source
는 다을지인입니다스터이를 하는 인터페이스입니다.Int63() int64
방법: 정확하고 우리가 가장 최근에 사용한 솔루션에 필요한 유일한 것입니다.
그래서 우리는 정말로 필요하지 않습니다.rand.Rand
중 함)rand
패지키), arand.Source
우리에겐 완벽하게 충분합니다.
var src = rand.NewSource(time.Now().UnixNano())
func RandStringBytesMaskImprSrc(n int) string {
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}
이 " " " " " " " " " " " " " " " " " " " " " " " " " " " 을 초기화( 필요가 Rand
의 시대의math/rand
않는 및 당사의 패키지)rand.Source
적절하게 초기화/시딩됨).
여기서 한 가지 더 주의해야 할 것은 패키지 문서입니다.math/rand
상태:
기본 소스는 여러 Groutine에서 동시에 사용해도 안전합니다.
가 따서기보소스가보다 .Source
은 에의해얻수을있는▁be▁by는있▁obtained▁that로 얻을 수 있습니다.rand.NewSource()
기본 소스는 동시 액세스/사용 시 안전성을 제공해야 하기 때문입니다.rand.NewSource()
이것을 제공하지 않습니다 (따라서.Source
더 빠를 가능성이 높습니다.)
활용하기strings.Builder
은 이의모솔은다반음환다니합을션루전든을 합니다.string
컨텐츠가 처음으로 슬라이스에 내장된 경우([]rune
창세기에, 그리고.[]byte
의 용액에서에 "다솔루로에변다니환서됩션음로다변"로 변환합니다."로 합니다.string
이 최종 변환은 슬라이스의 내용을 복사해야 합니다.string
값은 불변이며 변환이 복사본을 만들지 않는 경우 문자열의 내용이 원래 슬라이스를 통해 수정되지 않는다고 보장할 수 없습니다.자세한 내용은 utf8 문자열을 []바이트로 변환하는 방법을 참조하십시오.및 골랑: []바이트(문자열) vs []바이트(문자열).
소개된 Go strings.Builder
1.10은 우리가 사용할 수 있는 새로운 유형입니다.string
…과 유사한내부적으로 사용됩니다.[]byte
콘텐츠를 구축하고, 우리가 끝나면, 우리는 결승전을 얻을 수 있습니다.string
방법을 사용하여 값을 계산합니다.하지만 여기서 멋진 점은 위에서 말한 것과 같은 작업을 수행하지 않고도 이러한 작업을 수행할 수 있다는 것입니다.문자열의 내용을 빌드하는 데 사용되는 바이트 슬라이스가 노출되지 않으므로 생성된 "불변의" 문자열을 변경하기 위해 의도하지 않거나 악의적으로 수정할 수 없음을 보장합니다.
그래서 우리의 다음 아이디어는 임의의 문자열을 조각으로 만드는 것이 아니라, 그것의 도움으로 만드는 것입니다.strings.Builder
그래서 일단 우리가 끝나면, 우리는 그것의 사본을 만들지 않고도 결과를 얻고 반환할 수 있습니다.이는 속도 측면에서 도움이 될 수 있으며 메모리 사용 및 할당 측면에서도 분명히 도움이 될 것입니다.
func RandStringBytesMaskImprSrcSB(n int) string {
sb := strings.Builder{}
sb.Grow(n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
sb.WriteByte(letterBytes[idx])
i--
}
cache >>= letterIdxBits
remain--
}
return sb.String()
}
새 항목을 만든 후에 주의하십시오.strings.Buidler
우리는 그것의 메소드를 호출하여, 그것이 큰 크기의 내부 슬라이스를 할당하도록 했습니다(랜덤 문자를 추가할 때 재할당을 방지하기 위해).
"모방"strings.Builder
unsafe
strings.Builder
내서문 을작니다성으로 합니다.[]byte
우리가 했던 것과 똑같습니다.그래서 기본적으로 a를 통해 그것을 합니다.strings.Builder
우리가 바꾼 유일한 것은 약간의 오버헤드가 있습니다.strings.Builder
이는 슬라이스의 최종 복사를 방지하기 위한 것입니다.
strings.Builder
패키지를 사용하여 최종 복사본을 방지합니다.
// String returns the accumulated string.
func (b *Builder) String() string {
return *(*string)(unsafe.Pointer(&b.buf))
}
문제는 우리 스스로도 할 수 있다는 것입니다.그래서 여기서의 아이디어는 무작위 문자열을 만드는 것으로 다시 전환하는 것입니다.[]byte
하지만 우리가 끝났을 때, 그것을 로 변환하지 마세요.string
변환을 ▁a다▁to를 얻습니다.string
바이트 슬라이스를 문자열 데이터로 가리킵니다.
다음과 같은 방법을 사용할 수 있습니다.
func RandStringBytesMaskImprSrcUnsafe(n int) string {
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return *(*string)(unsafe.Pointer(&b))
}
사용하기 (9. 사용9rand.Read()
)
Go 1.7은 기능과 방법을 추가했습니다.더 나은 성능을 얻기 위해 한 단계에서 필요한 만큼의 바이트를 읽기 위해 이를 사용해야 합니다.
여기에는 작은 "문제"가 하나 있습니다. 몇 바이트가 필요한가요?우리는 이렇게 말할 수 있습니다: 출력 문자의 수만큼.문자 인덱스는 8비트(1바이트) 미만을 사용하기 때문에 이것은 상위 추정치라고 생각합니다.하지만 이 시점에서 우리는 이미 더 나빠지고 있으며(랜덤 비트를 얻는 것이 "어려운 부분"이기 때문에), 필요 이상의 것을 얻고 있습니다.
또한 모든 문자 인덱스의 동일한 분포를 유지하기 위해 사용할 수 없는 "가비지" 랜덤 데이터가 있을 수 있으므로 일부 데이터를 건너뛸 수 있으며, 따라서 모든 바이트 슬라이스를 통과할 때 부족하게 됩니다.우리는 "재귀적으로" 더 많은 랜덤 바이트를 얻어야 할 필요가 있습니다.그리고 이제 우리는 심지어 "단 한 번의 전화"를 잃고 있습니다.rand
패키지" 이점...
우리는 우리가 획득한 무작위 데이터의 사용을 "어느 정도" 최적화할 수 있습니다.math.Rand()
필요한 바이트 수(비트)를 추정할 수 있습니다. 한 글자가 필요합니다.letterIdxBits
비트, 그리고 우리는 필요합니다.n
편지, 그래서 우리는 필요합니다.n * letterIdxBits / 8.0
반올림한 바이트 수랜덤 인덱스를 사용할 수 없는 확률을 계산할 수 있으므로(위 참조), "더 가능성이 높은" 더 많은 것을 요청할 수 있습니다(사용할 수 없는 것으로 판명되면 프로세스를 반복합니다).예를 들어 바이트 슬라이스를 "비트 스트림"으로 처리할 수 있습니다. 여기에는 좋은 타사 lib가 있습니다. (공개:제가 저자입니다.
하지만 벤치마크 코드는 여전히 우리가 이기지 못하고 있음을 보여줍니다.그것은 왜 그럴까?
마지막 질문에 대한 대답은rand.Read()
루프를 사용하고 계속 호출합니다.Source.Int63()
통과된 슬라이스를 채울 때까지. 뜻입니까?RandStringBytesMaskImprSrc()
솔루션은 중간 버퍼 없이 복잡성을 증가시키지 않습니다.그렇기 때문에RandStringBytesMaskImprSrc()
왕의 자리에 남아 있습니다. 네.RandStringBytesMaskImprSrc()
되지 않은 동화않사를 합니다.rand.Source
와는 달리rand.Read()
하지만 그 추론은 여전히 적용됩니다; 그리고 우리가 사용한다면 증명된 것입니다.Rand.Read()
에 rand.Read()
(전자도 동기화되지 않음).
II. 벤치마크
이제 다양한 솔루션을 벤치마킹할 시간입니다.
진실의 순간:
BenchmarkRunes-4 2000000 723 ns/op 96 B/op 2 allocs/op
BenchmarkBytes-4 3000000 550 ns/op 32 B/op 2 allocs/op
BenchmarkBytesRmndr-4 3000000 438 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMask-4 3000000 534 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMaskImpr-4 10000000 176 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMaskImprSrc-4 10000000 139 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMaskImprSrcSB-4 10000000 134 ns/op 16 B/op 1 allocs/op
BenchmarkBytesMaskImprSrcUnsafe-4 10000000 115 ns/op 16 B/op 1 allocs/op
실행에서 바이트로 전환하는 것만으로도 성능이 즉시 24% 향상되고 메모리 요구량이 1/3로 떨어집니다.
rand.Intn()
및사용을 사용합니다.rand.Int63()
대신에 20%의 추가적인 향상을 제공합니다.
마스킹(큰 인덱스의 경우 반복)은 (반복 호출로 인해) 약간 느려집니다: -22%...
의 임의의 비트의 러개 63의개에서 를 대부분)때, (1개 63인 10랜) 을또덤비때트중사용모든할분대부는스나덱그의▁but또을때개용사▁(▁we할▁make▁random▁bits)▁when10▁(▁of▁all중▁the▁from▁63개▁indices▁useor▁of모▁most개든(.rand.Int63()
call): 그것은 큰 속도를 냅니다: 3배.
우리가 (이 아닌,한다면, (기본값이 아닌, 새로운)으로 됩니다.rand.Source
에 rand.Rand
우리는 다시 21%를 얻습니다.
가 할경우를 사용한다면,strings.Builder
속도는 3.5%에 불과하지만 메모리 사용량과 할당도 50%나 줄었습니다!좋네요!
가 감히 한다면,unsafe
에 strings.Builder
우리는 다시 14%를 얻습니다.
솔루션과 솔루션 비교: 최종솔과솔루션비교기초:RandStringBytesMaskImprSrcUnsafe()
보다 6.3배 더 빠릅니다.RandStringRunes()
6분의 1의 메모리와 절반의 할당을 사용합니다.임무 완수했어.
코드를 작성하면 됩니다.이 코드는 UTF-8로 인코딩될 때 문자가 모두 단일 바이트라는 것에 의존하고자 하는 경우 조금 더 단순할 수 있습니다.
package main
import (
"fmt"
"time"
"math/rand"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
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() {
fmt.Println(randSeq(10))
}
패키지 uniuri를 사용하면 암호화된 보안 균일(편향되지 않음) 문자열을 생성할 수 있습니다.
고지 사항:나는 패키지의 작성자입니다.
중복 결과를 최소화하는 간단한 솔루션:
import (
"fmt"
"math/rand"
"time"
)
func randomString(length int) string {
rand.Seed(time.Now().UnixNano())
b := make([]byte, length+2)
rand.Read(b)
return fmt.Sprintf("%x", b)[2 : length+2]
}
놀이터에서 확인하세요.
두 가지 가능한 옵션(물론 더 있을 수 있음):
당신은 할 수 .
crypto/rand
(/dev/urandom에서) 랜덤 바이트 배열 읽기를 지원하고 암호화 랜덤 생성을 위해 조정된 패키지입니다.http://golang.org/pkg/crypto/rand/ #example_Read를 참조하십시오. 하지만 일반적인 유사 프로세서 번호 생성보다 느릴 수 있습니다.임의의 숫자를 가져와서 md5나 이런 것을 사용하여 해시합니다.
암호화된 보안 난수를 사용하고 정확한 문자 집합이 유연한 경우(예: 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.Ceil(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.Ceil(float64(l)/2)))
rand.Read(buff)
str := hex.EncodeToString(buff)
return str[:l] // strip 1 extra character we get from odd length results
}
그러나 문자 집합에 base256 to baseN 인코더가 있는 경우 임의 문자 집합으로 확장할 수 있습니다.문자 집합을 나타내는 데 필요한 비트 수를 사용하여 동일한 크기를 계산할 수 있습니다.은 다음과 ratio = 8 / log2(len(charset))
).
이 두 가지 솔루션은 모두 안전하고 간단하며 속도가 빨라야 하며 암호화 엔트로피 풀을 낭비하지 마십시오.
여기 놀이터는 어떤 크기에도 적합하다는 것을 보여줍니다.https://play.golang.org/p/_yF_xxXer0Z
JavaScript 암호의 생성 암호에서 영감을 얻은 다른 버전:
package main
import (
"crypto/rand"
"fmt"
)
var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-"
func shortID(length int) string {
ll := len(chars)
b := make([]byte, length)
rand.Read(b) // generates len(b) random bytes
for i := 0; i < length; i++ {
b[i] = chars[int(b[i])%ll]
}
return string(b)
}
func main() {
fmt.Println(shortID(18))
fmt.Println(shortID(18))
fmt.Println(shortID(18))
}
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
.
이것이 나의 방법입니다) 당신이 원하는 대로 수학 랜드나 암호 랜드를 사용하세요.
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]
}
다음은 암호화된 보안 랜덤 문자열을 위한 간단하고 성능이 뛰어난 솔루션입니다.
package main
import (
"crypto/rand"
"unsafe"
"fmt"
)
var alphabet = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func main() {
fmt.Println(generate(16))
}
func generate(size int) string {
b := make([]byte, size)
rand.Read(b)
for i := 0; i < size; i++ {
b[i] = alphabet[b[i] % byte(len(alphabet))]
}
return *(*string)(unsafe.Pointer(&b))
}
벤치마크
Benchmark 95.2 ns/op 16 B/op 1 allocs/op
func Rand(n int) (str string) {
b := make([]byte, n)
rand.Read(b)
str = fmt.Sprintf("%x", b)
return
}
나는 보통 자본화 여부에 대한 선택권이 필요하면 이렇게 합니다.
func randomString(length int, upperCase bool) string {
rand.Seed(time.Now().UnixNano())
var alphabet string
if upperCase {
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
} else {
alphabet = "abcdefghijklmnopqrstuvwxyz"
}
var sb strings.Builder
l := len(alphabet)
for i := 0; i < length; i++ {
c := alphabet[rand.Intn(l)]
sb.WriteByte(c)
}
return sb.String()
}
그리고 만약 대문자가 필요하지 않다면 이렇게.
func randomString(length int) string {
rand.Seed(time.Now().UnixNano())
var alphabet string = "abcdefghijklmnopqrstuvwxyz"
var sb strings.Builder
l := len(alphabet)
for i := 0; i < length; i++ {
c := alphabet[rand.Intn(l)]
sb.WriteByte(c)
}
return sb.String()
}
허용된 문자 풀에 몇 개의 문자를 추가할 의향이 있다면 io를 통해 임의의 바이트를 제공하는 모든 항목에서 코드를 작동시킬 수 있습니다.서는 더리를 합니다. 여기서 우리가 사용하고 있습니다.crypto/rand
.
// len(encodeURL) == 64. This allows (x <= 265) x % 64 to have an even
// distribution.
const encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
// A helper function create and fill a slice of length n with characters from
// a-zA-Z0-9_-. It panics if there are any problems getting random bytes.
func RandAsciiBytes(n int) []byte {
output := make([]byte, n)
// We will take n bytes, one byte for each character of output.
randomness := make([]byte, n)
// read all random
_, err := rand.Read(randomness)
if err != nil {
panic(err)
}
// fill output
for pos := range output {
// get random item
random := uint8(randomness[pos])
// random % 64
randomPos := random % uint8(len(encodeURL))
// put into output
output[pos] = encodeURL[randomPos]
}
return output
}
이것은 제 앱에서 인증서 번호를 생성할 때 사용했던 샘플 코드입니다.
func GenerateCertificateNumber() string {
CertificateLength := 7
t := time.Now().String()
CertificateHash, err := bcrypt.GenerateFromPassword([]byte(t), bcrypt.DefaultCost)
if err != nil {
fmt.Println(err)
}
// Make a Regex we only want letters and numbers
reg, err := regexp.Compile("[^a-zA-Z0-9]+")
if err != nil {
log.Fatal(err)
}
processedString := reg.ReplaceAllString(string(CertificateHash), "")
fmt.Println(string(processedString))
CertificateNumber := strings.ToUpper(string(processedString[len(processedString)-CertificateLength:]))
fmt.Println(CertificateNumber)
return CertificateNumber
}
탁월한 솔루션의 후속 조치로 아래에서 사용하고 있습니다.
func RandStringBytesMaskImprRandReaderUnsafe(length uint) (string, error) {
const (
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
charIdxBits = 6 // 6 bits to represent a letter index
charIdxMask = 1<<charIdxBits - 1 // All 1-bits, as many as charIdxBits
charIdxMax = 63 / charIdxBits // # of letter indices fitting in 63 bits
)
buffer := make([]byte, length)
charsetLength := len(charset)
max := big.NewInt(int64(1 << uint64(charsetLength)))
limit, err := rand.Int(rand.Reader, max)
if err != nil {
return "", err
}
for index, cache, remain := int(length-1), limit.Int64(), charIdxMax; index >= 0; {
if remain == 0 {
limit, err = rand.Int(rand.Reader, max)
if err != nil {
return "", err
}
cache, remain = limit.Int64(), charIdxMax
}
if idx := int(cache & charIdxMask); idx < charsetLength {
buffer[index] = charset[idx]
index--
}
cache >>= charIdxBits
remain--
}
return *(*string)(unsafe.Pointer(&buffer)), nil
}
func BenchmarkBytesMaskImprRandReaderUnsafe(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
const length = 16
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
RandStringBytesMaskImprRandReaderUnsafe(length)
}
})
}
저는 많은 프로젝트에서 구글의 uuid 라이브러리를 사용하고 있으며, 이를 통해 다음과 같은 작업을 쉽게 수행할 수 있습니다.
uuid.NewString()[:length]
그것은 최소한 상자를 확인해서 가장 단순하게 만듭니다.
https://go.dev/play/p/5sWDsGqglAi
/*
korzhao
*/
package rand
import (
crand "crypto/rand"
"math/rand"
"sync"
"time"
"unsafe"
)
// Doesn't share the rand library globally, reducing lock contention
type Rand struct {
Seed int64
Pool *sync.Pool
}
var (
MRand = NewRand()
randlist = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
)
// init random number generator
func NewRand() *Rand {
p := &sync.Pool{New: func() interface{} {
return rand.New(rand.NewSource(getSeed()))
},
}
mrand := &Rand{
Pool: p,
}
return mrand
}
// get the seed
func getSeed() int64 {
return time.Now().UnixNano()
}
func (s *Rand) getrand() *rand.Rand {
return s.Pool.Get().(*rand.Rand)
}
func (s *Rand) putrand(r *rand.Rand) {
s.Pool.Put(r)
}
// get a random number
func (s *Rand) Intn(n int) int {
r := s.getrand()
defer s.putrand(r)
return r.Intn(n)
}
// bulk get random numbers
func (s *Rand) Read(p []byte) (int, error) {
r := s.getrand()
defer s.putrand(r)
return r.Read(p)
}
func CreateRandomString(len int) string {
b := make([]byte, len)
_, err := MRand.Read(b)
if err != nil {
return ""
}
for i := 0; i < len; i++ {
b[i] = randlist[b[i]%(62)]
}
return *(*string)(unsafe.Pointer(&b))
}
24.0ns/op 16 B/op 1 할당/
package main
import (
"encoding/base64"
"fmt"
"math/rand"
"time"
)
// customEncodeURL is like `bas64.encodeURL`
// except its made up entirely of uppercase characters:
const customEncodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKL"
// Random generates a random string.
// It is not cryptographically secure.
func Random(n int) string {
b := make([]byte, n)
rand.Seed(time.Now().UnixNano())
_, _ = rand.Read(b) // docs say that it always returns a nil error.
customEncoding := base64.NewEncoding(customEncodeURL).WithPadding(base64.NoPadding)
return customEncoding.EncodeToString(b)
}
func main() {
fmt.Println(Random(16))
}
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))
}
벤치마크RandStr16-820000000 68.1ns/op 16 B/op 1 할당/op
언급URL : https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go
'it-source' 카테고리의 다른 글
UI 텍스트 필드의 시작 부분에 공백 만들기 (0) | 2023.05.01 |
---|---|
설치 오류: INSTALL_FAILED_OLDER_SDK (0) | 2023.05.01 |
Postgre에 비해 얼마나 큰가.SQL 테이블? (0) | 2023.05.01 |
Powershell의 Git 분기에 대한 탭 완료 추가 (0) | 2023.05.01 |
SQL Server 테이블의 기본 키를 어떻게 나열합니까? (0) | 2023.05.01 |