Ruby에서 임의의 문자열을 생성하는 방법


746

현재 "A".. "Z"에 대한 8 자리 의사 난수 대문자 문자열을 생성 중입니다.

value = ""; 8.times{value  << (65 + rand(25)).chr}

그러나 그것은 깨끗해 보이지 않으며 단일 진술이 아니기 때문에 인수로 전달 될 수 없습니다. 대소 문자가 혼합 된 문자열 "a".. "z"와 "A".. "Z"를 얻으려면 다음과 같이 변경했습니다.

value = ""; 8.times{value << ((rand(2)==1?65:97) + rand(25)).chr}

하지만 쓰레기처럼 보입니다.

누구든지 더 나은 방법이 있습니까?


"당신은 그것이 단일 진술이 아니기 때문에 그것이 인수로 전달 될 수 없기 때문에"왜 당신이 신경 쓰는지 이해하지 못합니다. 왜 유틸리티 또는 도우미 메소드로 만들지 않습니까?
David J.

1
사용자 비밀번호를 재설정하는 방법이 있으며 새 비밀번호에 대한 인수가 있다고 가정하십시오. 위의 코드에서 tmp 변수가 필요한 임의의 문자열을 전달하고 싶습니다. 단일 명령문 예제에서는 모든 것을 하나의 라이너로 수행 할 수 있습니다. 물론 유틸리티 방법이 장기적으로 좋을 수도 있습니다. 특히 여기저기서 비슷해야 할 경우가 있지만 때로는 한 번만 수행하면됩니다.
Jeff

아니요, 임시 변수를 사용할 필요가 없습니다. 이것을 시도하십시오 : reset_user_password!(random_string)어디def random_string; SecureRandom.urlsafe_base64(20) end
David J.

8 글자는 부끄럽게 약한 비밀번호입니다. md5sum이 주어지면 현대 PC는 30 초 안에 암호를 복구 할 수 있습니다. 방법에 대해 뭔가 이상securerandom.urlsafe_base64
대령 패닉

1
왜 이렇게 많은 답변이 있습니까? 유용한 질문은 아니지만 그것이 어떻게 관심을 끌 었는지 궁금합니다.
Lou

답변:


969
(0...8).map { (65 + rand(26)).chr }.join

나는 너무 많은 시간을 골프를 씁니다.

(0...50).map { ('a'..'z').to_a[rand(26)] }.join

그리고 더 혼란 스럽지만 더 유연하고 더 적은 사이클을 낭비하는 마지막 것 :

o = [('a'..'z'), ('A'..'Z')].map(&:to_a).flatten
string = (0...50).map { o[rand(o.length)] }.join

204
34 자 이상으로 빠르게 : ('a'..'z').to_a.shuffle[0,8].join. Ruby> = 1.9 ~이 필요합니다 shuffle.
fny

20
드라이버를 가지고 있지 않으면 기존 라이브러리를 활용하는 것이 좋습니다. SecureRandom다른 답변에서 하나의 예를 참조하십시오 .
David J.

28
@faraz 방법이 기능적으로 동일하지 않으며 대체와 무작위가 아닙니다.
michaeltwofish

35
[*('a'..'z'),*('0'..'9')].shuffle[0,8].join문자와 숫자가 모두있는 임의의 문자열을 생성합니다.
Robin

31
rand결정적이고 예측 가능합니다. 비밀번호를 생성 할 때 이것을 사용하지 마십시오! SecureRandom대신 솔루션 중 하나를 사용하십시오 .
연필

800

SecureRandom을 사용하지 않는 이유는 무엇입니까?

require 'securerandom'
random_string = SecureRandom.hex

# outputs: 5b5cd0da3121fc53b4bc84d0c8af2e81 (i.e. 32 chars of 0..9, a..f)

SecureRandom에는 다음과 같은 방법도 있습니다.

  • base64
  • random_bytes
  • random_number

참조 : http://ruby-doc.org/stdlib-1.9.2/libdoc/securerandom/rdoc/SecureRandom.html


11
base64는 그의 예제와 같이 16 진수가 아닙니다
Jeff Dickey

67
그건 그렇고, 그것은 1.9와 최근 1.8 버전의 stdlib의 일부이므로, require 'securerandom'이 깔끔한 SecureRandom도우미 를 얻을 수 있습니다 :)
J -_- L

21
BTW SecureRandom은 버전 3.2의 ActiveSupport에서 제거되었습니다. 변경 로그에서 : "표준 라이브러리에서 SecureRandom을 위해 ActiveSupport :: SecureRandom을 제거했습니다."
Marc

15
SecureRandom.random_number(36**12).to_s(36).rjust(12, "0")항상 12 자 길이 인 0-9a-z (36 자)의 문자열을 생성합니다. 12를 원하는 길이로 변경하십시오. 불행히도을 사용하여 AZ를 얻는 방법은 없습니다 Integer#to_s.
Gerry Shaw

1
@ stringo0 잘못되었습니다. +fGH1URL 을 통과 하려면 URL 을 통과 하는 모든 값처럼 URL을 인코딩하면됩니다. %2BfGH1
nzifnab

247

나는 이것을 보장 된 최대 길이로 임의의 URL 친화적 인 문자열을 생성하는 데 사용합니다 :

rand(36**length).to_s(36)

소문자 az 및 0-9의 임의 문자열을 생성합니다. 매우 사용자 정의 할 수는 없지만 짧고 깨끗합니다.


4
가장 짧은 버전의 경우 +1 (외부 바이너리를 호출하지 않습니다 ^^). 임의의 문자열이 공개되지 않으면 때로는 때로는 rand.to_s; 못생긴,하지만 작동합니다.
Jo Liss

12
이것은 훌륭한 솔루션입니다 (그리고 빠르고도)하지만, 때때로 캐릭터를 생성합니다 아래에 length ~ (40)에 대략 한 번, 길이
브라이언 E

7
@Brian E 이것은 원하는 숫자를 보장합니다 : (36**(length-1) + rand(36**length)).to_s(36). 기준 36으로 변환 된 36 ** (길이 -1)은 10 ** (길이 -1)이며 원하는 숫자 길이를 가진 가장 작은 값입니다.
Eric Hu

11
여기에 항상 원하는 길이의 토큰을 생산하는 버전입니다 :(36**(length-1) + rand(36**length - 36**(length-1))).to_s(36)
아드 Jarthon

3
레일 4 루비 2.1.1에서 나를 위해 오류 출력이 옷을 사는 : NameError: undefined local variable or method 주를위한 길이 'Object`
kakubei

175

이 솔루션은 활성화 코드에 대해 쉽게 읽을 수있는 문자열을 생성합니다. 8과 B, 1과 I, 0과 O, L과 1을 혼동하는 사람들을 원하지 않았습니다.

# Generates a random string from a set of easily readable characters
def generate_activation_code(size = 6)
  charset = %w{ 2 3 4 6 7 9 A C D E F G H J K M N P Q R T V W X Y Z}
  (0...size).map{ charset.to_a[rand(charset.size)] }.join
end

3
'U'가 모호합니까, 아니면 오타입니까?
gtd

10
@gtd-p. U와 V는 ambigvovs입니다.
colinm

1
@colinm V는 거기에 있습니다.
gtd

8
보안을 유지하기 위해 SecureRandom.random_number(charset.size)대신rand(charset.size)
gtd

1
난 그냥 소문자 및 / 또는 일부 특수 문자 (Shift + NUM)를 추가하여,이를 개선하기 위해 노력하고, 다음과 같은 목록을했습니다 : %w{ A C D E F G H J K L M N P Q R T W X Y Z 2 3 4 6 7 9 ! @ # $ % ^ & * +}그리고 %w{ A D E F G H J L N Q R T Y a d e f h n r y 2 3 4 7 ! @ # $ % ^ & * }어떻게 일종의 흥미 (첫 번째 목록 낮은 경우를 포함하지 않지만, 더 오래입니다) 운동했다.
dkniffin

130

다른 사람들은 비슷한 것을 언급했지만 URL 안전 기능을 사용합니다.

require 'securerandom'
p SecureRandom.urlsafe_base64(5) #=> "UtM7aa8"
p SecureRandom.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
p SecureRandom.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="

결과에는 AZ, az, 0-9,“-”및“_”이 포함될 수 있습니다. 패딩이 true 인 경우에도 "="가 사용됩니다.


이것이 바로 내가 찾던 것입니다. 감사!
Dan Williams

69

Ruby 2.5부터는 SecureRandom.alphanumeric다음 과 같이 정말 쉽습니다 .

len = 8
SecureRandom.alphanumeric(len)
=> "larHSsgL"

AZ, az 및 0-9를 포함하는 임의의 문자열을 생성하므로 대부분의 사용 사례에 적용 할 수 있습니다. 그리고 그것들은 무작위로 안전하게 생성되어 이익이 될 수도 있습니다.


이것은 가장 많이 찬성하는 솔루션과 비교하기위한 벤치 마크입니다.

require 'benchmark'
require 'securerandom'

len = 10
n = 100_000

Benchmark.bm(12) do |x|
  x.report('SecureRandom') { n.times { SecureRandom.alphanumeric(len) } }
  x.report('rand') do
    o = [('a'..'z'), ('A'..'Z'), (0..9)].map(&:to_a).flatten
    n.times { (0...len).map { o[rand(o.length)] }.join }
  end
end

                   user     system      total        real
SecureRandom   0.429442   0.002746   0.432188 (  0.432705)
rand           0.306650   0.000716   0.307366 (  0.307745)

따라서 rand솔루션은 시간의 약 3/4 만 걸립니다 SecureRandom. 많은 문자열을 생성하면 문제가 될 수 있지만 때때로 임의의 문자열을 생성하면 호출하기 쉽고 명시 적으로하기 때문에 항상보다 안전한 구현을 사용합니다.


48
[*('A'..'Z')].sample(8).join

임의의 8 자 문자열 생성 (예 : NVAYXHGR)

([*('A'..'Z'),*('0'..'9')]-%w(0 1 I O)).sample(8).join

0 / 1 / I / O를 제외하고 임의의 8 자 문자열 (예 : 3PH4SWF2)을 생성하십시오. 루비 1.9


4
문제의 유일한 결과는 결과의 각 문자가 고유하다는 것입니다. 가능한 값을 제한합니다.
tybro0103

1
기능 요청 이 진행되면 Ruby 1.9.x는 대체없이 샘플링을위한 #sample 및 교체를 통한 샘플링을위한 #choice로 끝날 수 있습니다.
David J.

이것은 오류입니다 [*("A".."Z")]'. 필요한 것 같습니다 ... ; ((단일 qoutes 아님))
jayunit100

내가 어떻게 stub이것을 rspec통과 할 수 있는지 말해 줄 수 있습니까 ?
Sinscary

31

나는 이것을 어디에서 찾았는지 기억할 수 없지만 그것은 나에게 가장 좋고 가장 적은 프로세스 인 것 같습니다.

def random_string(length=10)
  chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789'
  password = ''
  length.times { password << chars[rand(chars.size)] }
  password
end


이것에서 0과 1이 누락되지 않았습니까?
sunnyrjuneja

내가 찾은 곳인 것 같아
Travis Reeder

그리고 나중에 0과 1이없는 것처럼 보입니다.
Travis Reeder

4
01OI해당 문자가 모호하기 때문에 의도적으로 실종되었다. 이러한 종류의 코드를 사용하여 사용자가 복사해야하는 문자 세트를 생성하는 경우 컨텍스트와 구별하기 어려운 문자를 제외하는 것이 가장 좋습니다.
앤드류

28
require 'securerandom'
SecureRandom.urlsafe_base64(9)

SecureRandom은 훌륭한 라이브러리입니다. 나는 그것이 거기에 있다는 것을 몰랐다. 감사.
Dogweather

오래된 스쿨 (그리고 Random.new.bytes(9)
못생긴

4
btw에서 urlsafe_base64는 표시된 길이의 약 4/3 인 문자열을 반환합니다. 정확히 n 자 길이의 문자열을 얻으려면 다음을 시도하십시오n=9 ; SecureRandom.urlsafe_base64(n)[0..n-1]
tardate

25

지정된 길이의 문자열을 원하면 다음을 사용하십시오.

require 'securerandom'
randomstring = SecureRandom.hex(n)

그것은 길이의 임의의 문자열 생성합니다 2n함유 0-9하고a-f


length의 문자열을 생성하지 않고 n실제로 4/3의 문자열을 생성합니다 n.
Omar Ali

@OmarAli 당신이 틀 렸습니다. 루비 문서에 따르면 .hex2n입니다. 문서 : SecureRandom.hex는 임의의 16 진 문자열을 생성합니다. 인수 n 은 생성 될 난수의 길이 (바이트)를 지정합니다. 결과 16 진 문자열의 길이는 n의 두 배입니다 .
Rihards


11
require 'sha1'
srand
seed = "--#{rand(10000)}--#{Time.now}--"
Digest::SHA1.hexdigest(seed)[0,8]

흥미롭지 만 계산 비용이 약간 비쌉니다.
Jeff

제한된 문자 범위에 대한 용량도 없습니다.
Kent Fredric

3
16 진수 다이제스트는 0-9와 af 문자 만 반환합니다.
webmat

11

길이가 8 인 임의 문자열에 대한 한 줄 간단한 코드는 다음과 같습니다.

 random_string = ('0'..'z').to_a.shuffle.first(8).join

길이가 8 인 임의의 비밀번호에도 사용할 수 있습니다.

random_password = ('0'..'z').to_a.shuffle.first(8).join

2
이 방법은 문자를 반복하지 않으므로 비밀번호를 생성하는 데 사용해서는 안됩니다. 따라서 P(36, 8) / 36^8 = 0.48 글자 (~ 2x 더 쉬운 무차별) P(36, 25) / 36^25 = 0.00001에 대해 가능한 문자 공간 또는 25 자 (~ 100,000x 더 무차별하기 쉬운 ) 에 대한 가능한 문자 공간 만 사용합니다.
Glacials

10

루비 1.9+ :

ALPHABET = ('a'..'z').to_a
#=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

10.times.map { ALPHABET.sample }.join
#=> "stkbssowre"

# or

10.times.inject('') { |s| s + ALPHABET.sample }
#=> "fdgvacnxhc"

1
map솔루션은 정말 좋은 것입니다!
Misha Moroshko

#sample원하는 요소 수를 요청할 수 있습니다 . 예 : ALPHABET.sample(10).join... ruby-doc.org/core-2.4.0/Array.html#method-i-sample
odlp

실제로 잘못되었습니다. sample(10)10 개의 고유 한 샘플을 제공합니다. 그러나 당신은 그들이 반복되도록하고 싶습니다. 하지만 사용하는 것이 Array.new(10).map성능
사샤 Zejnilović

소문자와 대문자가 모두 영숫자 원했습니다. 또한 사용 Array.new및 해당 블록 구문으로 전환했습니다 . Array.new(20) { [*'0'..'9', *'a'..'z', *'A'..'Z'].sample }.join
taylorthurlow

8

알아두기 : rand공격자가 예측할 수 있으므로 안전하지 않을 수 있습니다. 암호 생성을위한 것이라면 SecureRandom을 반드시 사용해야합니다. 나는 이와 같은 것을 사용한다 :

length = 10
characters = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a

password = SecureRandom.random_bytes(length).each_char.map do |char|
  characters[(char.ord % characters.length)]
end.join

아마도 "가장 안전한"솔루션 일 것입니다. SecureRandom은 운영 체제에서 제공하는 기본 보안 API를 사용하려고합니다. OpenSSL이 있으면 그것을 사용하고 Windows에 있으면 가장 좋은 옵션이됩니다. 특히이 솔루션을 사용하면 사용할 문자 세트를 지정할 수 있기 때문에 좋아합니다. 문자 세트가 바이트의 최대 값 인 255보다 긴 경우에는 작동하지 않지만 문서에서 SecureRandom의 소스 코드를 보는 것이 좋습니다. ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/ rdoc /…
Breedly

8

길이가 8 인 임의 비밀번호에 대한 간단한 코드는 다음과 같습니다.

rand_password=('0'..'z').to_a.shuffle.first(8).join

7

내가 사용하고 싶은 다른 방법 :

 rand(2**256).to_s(36)[0..7]

ljust정확한 문자열 길이에 대해 편집증이 있다면 추가하십시오 .

 rand(2**256).to_s(36).ljust(8,'a')[0..7]

rand (2 ** 64) .to_s (36) [-10,10]
Jeff

6
SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')

Devise에서 무언가


.tr('+/=lIO0', 'pqrsxyz')?를 사용하여 문자열 문자를 바꾸는 이유는 무엇 입니까?
miguelcobain

URL이 안전하지 않기 때문에 특수 문자입니다. l / I 또는 O / 0은 읽을 수있는 사용자 비밀번호를 생성하는 기술을 사용하는 경우 매우 혼동되기 때문에 매우 복잡합니다.
ToniTornado

이 기능은 특정 문자에 약간의 편견이 있습니다. 또한 다른 길이 (예 : 16)의 경우 마지막 문자는 무작위가 아닙니다. 이를 피할 수있는 방법이 있습니다. SecureRandom.base64 (64) .tr ( '+ / = lIO01', '') [0,16]
Shai Coleman

5

나는 이것이 간결함, 명확성 및 수정 용이성의 훌륭한 균형이라고 생각합니다.

characters = ('a'..'z').to_a + ('A'..'Z').to_a
# Prior to 1.9, use .choice, not .sample
(0..8).map{characters.sample}.join

쉽게 수정

예를 들어 숫자 포함 :

characters = ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a

대문자 16 진수 :

characters = ('A'..'F').to_a + (0..9).to_a

정말 인상적인 캐릭터 배열 :

characters = (32..126).to_a.pack('U*').chars.to_a

1
대문자 + 숫자를 사용하고 "혼란스러운"문자를 ​​제거하는 것이 좋습니다. charset = (1..9) .to_a.concat (( 'A'.. 'Z'). to_a) .reject {| a | [0, 1, 'O', 'I']. include? (a)} (0 ... size) .map {charset [rand (charset.size)]} .join
luster

5

여기에 내 센트를 추가하면 ...

def random_string(length = 8)
  rand(32**length).to_s(32)
end

3
NB : 항상 + 길이 + 긴 문자열을 반환하는 것은 아닙니다. 더 짧을 수도 있습니다. 다음에 의해 반환 된 수에 따라 다릅니다.rand
tardate

5

String#randomRuby Gem의 패싯에서 사용할 수 있습니다 facets.

기본적으로이 작업을 수행합니다.

class String
  def self.random(len=32, character_set = ["A".."Z", "a".."z", "0".."9"])
    characters = character_set.map { |i| i.to_a }.flatten
    characters_len = characters.length
    (0...len).map{ characters[rand(characters_len)] }.join
  end
end

4

내가 가장 좋아하는 것은 (:A..:Z).to_a.shuffle[0,8].join입니다. 셔플에는 Ruby> 1.9가 필요합니다.


4

이 솔루션에는 외부 종속성이 필요하지만 다른 솔루션보다 더 예쁘게 보입니다.

  1. gem faker 설치
  2. Faker::Lorem.characters(10) # => "ang9cbhoa8"

4

주어진:

chars = [*('a'..'z'),*('0'..'9')].flatten

단일 표현식은 인수로 전달 될 수 있으며 중복 문자를 허용합니다.

Array.new(len) { chars.sample }.join

3

나는 지금까지 레이더의 대답이 가장 좋다고 생각합니다. 나는 다음과 같이 조금 조정했다.

CHARS = ('a'..'z').to_a + ('A'..'Z').to_a
def rand_string(length=8)
  s=''
  length.times{ s << CHARS[rand(CHARS.length)] }
  s
end

왜 사용하지 (CHARS*length).sample(length).join않습니까?
niels 2016 년

@niels이 제안은 반복되지 않은 문자를 위해 가중치 문자열을 생성합니다 . 예를 들어, 경우는 CHARS=['a','b']다음 방법은 생성하는 것 "aa"또는 "bb"시간의 33 % 만 "ab"또는 "ba"시간의 67 %. 어쩌면 그것은 문제가되지 않지만 염두에 두어야 할 가치가 있습니다!
Tom Lord

좋은 지적, @TomLord, 나는 내가 실제로 몰랐 생각 나는 (내가 인정해야하지만 나는 전혀 것을 게시물을 기억하지 않습니다 : D) 그 제안을 게시 할 때
닐스

3
''.tap {|v| 4.times { v << ('a'..'z').to_a.sample} }

3

내 2 센트 :

  def token(length=16)
    chars = [*('A'..'Z'), *('a'..'z'), *(0..9)]
    (0..length).map {chars.sample}.join
  end


3

3 가지 범위로 구성된 임의 문자열에 대한 2 가지 솔루션 :

(('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a).sample(8).join

([*(48..57),*(65..90),*(97..122)]).sample(8).collect(&:chr)*""

각 범위에서 하나의 문자.

대문자, 소문자 및 숫자가 하나 인 임의의 암호를 만드는 등 각 범위에서 하나 이상의 문자가 필요한 경우 다음과 같이 할 수 있습니다.

( ('a'..'z').to_a.sample(8) + ('A'..'Z').to_a.sample(8) + (0..9).to_a.sample(8) ).shuffle.join 
#=> "Kc5zOGtM0H796QgPp8u2Sxo1"

그리고 각 범위의 특정 수를 적용하려면 다음과 같이 할 수 있습니다.( ('a'..'z').to_a.sample(8) + ('A'..'Z').to_a.sample(8) + (0..9).to_a.sample(8) + [ "%", "!", "*" ].sample(8) ).shuffle.join #=> "Kc5zOGtM0*H796QgPp%!8u2Sxo1"
Joshua Pinter

3

최근 62 자에서 8 바이트 임의 문자열을 생성하기 위해 이와 같은 작업을 수행했습니다. 문자는 0-9, az, AZ입니다. 나는 배열을 8 번 반복하고 배열에서 임의의 값을 선택했습니다. 이것은 Rails 앱 안에있었습니다.

str = ''
8.times {|i| str << ARRAY_OF_POSSIBLE_VALUES[rand(SIZE_OF_ARRAY_OF_POSSIBLE_VALUES)] }

이상한 것은 내가 좋은 수의 복제본을 얻었다는 것입니다. 이제 무작위로 이것은 거의 일어나지 않아야합니다. 62 ^ 8은 거대하지만 db의 1200 개 정도의 코드 중 많은 수가 중복되었습니다. 나는 그들이 서로의 시간 경계에서 일어나는 것을 보았습니다. 다시 말하면 12:12:23 및 2:12:22에서 듀플 또는 이와 유사한 것을 볼 수 있습니다 ... 시간이 문제인지 아닌지 확실하지 않습니다.

이 코드는 ActiveRecord 객체를 만들기 전에 작성되었습니다. 레코드가 작성되기 전에이 코드가 실행되고 '고유 한'코드가 생성됩니다. DB의 항목은 항상 안정적으로 생성되었지만 str위의 줄에있는 코드 는 너무 자주 복제되었습니다.

나는 작은 지연 으로이 위의 줄을 100000 회 반복 실행하는 스크립트를 만들었으므로 매시간 어떤 종류의 반복 패턴을보기를 기대하면서 3-4 시간이 걸리지 만 아무것도 보지 못했습니다. Rails 앱에서 왜 이런 일이 발생했는지 전혀 모르겠습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.