Rubyでランダムな文字列を生成する方法


747

私は現在、 "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}

ゴミのようです。

誰かがより良い方法を持っていますか?


「単一のステートメントではないため、引数として渡すことはできない」と気にされた理由がわかりません。なぜそれをユーティリティまたはヘルパーメソッドにしないのですか?
デビッドJ.

1
ユーザーのパスワードをリセットするメソッドがあり、新しいパスワードの引数があるとします。ランダムな文字列を渡したいのですが、上記のコードではtmp変数が必要ですが、以下の1つのステートメントの例では、すべてを1つのライナーとして実行できます。確かに、ユーティリティメソッドは長期的に見れば良いでしょう。特に、あちこちで同様の方法が必要な場合はそうですが、場合によっては、一度だけ実行したい場合もあります。
ジェフ

いいえ、一時変数を使用する必要はありません。これを試してください:reset_user_password!(random_string)どこdef random_string; SecureRandom.urlsafe_base64(20) end
David J.

8文字は恥ずべきことに弱いパスワードです。md5sumを考えると、最新のPCは30秒でパスワードを回復できます。より長いものはどうですかsecurerandom.urlsafe_base64
大佐パニック

1
なぜこれはそれほど多くの答えを持っているのですか?それは有用な質問ではないというわけではありませんが、それがどのように注目を集めているのか興味があります。
ルー

回答:


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

私はゴルフに時間をかけすぎています。

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

そして、最後の1つはさらに混乱しますが、より柔軟性があり、無駄なサイクルが少なくなります。

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

205
34文字と超高速:('a'..'z').to_a.shuffle[0,8].join。Ruby> = 1.9が必要になることに注意してくださいshuffle
fny 2012年

20
独自のライブラリをロールするドライバがない限り、既存のライブラリを活用することをお勧めします。SecureRandom1つの例として、他の回答を参照してください。
デビッドJ.

28
@farazあなたの方法は機能的に同じではありません、それは置換でランダムではありません。
michaeltwofish 2012

35
[*('a'..'z'),*('0'..'9')].shuffle[0,8].join文字と数字の両方でランダムな文字列を生成します。
Robin

31
rand確定的で予測可能です。これをパスワードの生成に使用しないでください!SecureRandom代わりにソリューションの1つを使用してください。
2013年

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
@ブライアン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
これにより、Rails 4およびRuby 2.1.1でエラーが出力されます:NameError: undefined local variable or method main: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-はい。UとVはambigvovsです。
colim 2013

1
@colinm Vもそこにいます。
2013

8
安全のために、次のSecureRandom.random_number(charset.size)代わりに使用することもできますrand(charset.size)
gtd

1
私はちょうど下ケースおよび/または一部の特殊文字(シフト+ 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 2014年

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

ランダムな8文字の文字列(3PH4SWF2など)を生成し、0/1 / I / Oを除外します。Ruby 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

7
たぶんここでそれを見つけましたか?travisonrails.com/2007/06/07/generate-random-text-with-ruby
deadwards

これには0と1が欠けていませんか?
sunnyrjuneja 2013年

それは私がそれを見つけた場所かもしれないように見えます。
Travis Reeder

そして、そうです、それは0と1が欠けているように見えます。
Travis Reeder

4
し、かつそれらの文字があいまいであるため、意図的に欠落していました。この種のコードを使用して、ユーザーがコピーする必要のある文字のセットを生成している場合は、文脈から区別するのが難しい可能性がある文字を除外するのが最善です。01OI
Andrew

28
require 'securerandom'
SecureRandom.urlsafe_base64(9)

SecureRandomは優れたライブラリです。そこにあるとは知りませんでした。ありがとう。
Dogweather 2012

古いskool(および醜い)代替:Random.new.bytes(9)
tardate

4
ところで、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


長さの文字列を生成するのではなくn、実際にはの4/3の文字列を生成しますn
Omar Ali

@OmarAliあなたは間違っている。Rubyのドキュメントによると、.hex2nの場合です。ドキュメント:SecureRandom.hexはランダムな16進数文字列を生成します。引数nは、生成される乱数の長さをバイト単位で指定します。結果の16進数ストリングの長さはnの 2倍です。
Rihards


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

興味深いが、かなり計算コストが高い
Jeff

また、キャラクターの限られた範囲の容量はありません。
ケントフレドリック

3
16進数のダイジェストは0〜9およびa〜fの文字のみを返すことに注意してください。
webmat 2008

11

長さ8のランダム文字列の1行の単純なコードを次に示します。

 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.4、使用できる文字スペースは8文字(ブルートフォースで約2倍簡単)またはP(36, 25) / 36^25 = 0.0000125文字(ブルートフォースで簡単に約100,000 倍)可能な文字スペースのみです。
Glacials 2017

10

Ruby 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 /…
ブリードリー2017

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')

工夫からの何か


なぜ文字列文字を置き換えるの.tr('+/=lIO0', 'pqrsxyz')ですか?
ミゲルコバン2013

特殊文字はURLセーフではないため。また、l / IまたはO / 0は、読み取り可能なユーザーパスワードを生成する手法を使用すると混乱しやすくなるためです。
ToniTornado 2013年

その関数は、特定の文字に少し偏っています。また、他の長さ(16など)の場合、最後の文字はランダムになりません。これを回避する方法を次に示します。SecureRandom.base64(64).tr( '+ / = lIO01'、 '')[0,16]
Shai Coleman 14年

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
光沢

5

ここに私のセントを追加するだけです...

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

3
注意:これは、必ずしも正確に+ length + longの文字列を返すわけではありません-より短い場合があります。それは返される数に依存します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です。shuffleには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

@nielsこの提案は、 繰り返されない文字を優先して重み付けされた文字列ます。たとえば、もしCHARS=['a','b']、あなたの方法は、生成する"aa"か、"bb"時間のわずか33%が、"ab"または"ba"時間の67%。多分それは問題ではないかもしれませんが、心に留めておく価値があります!
Tom Lord

良い点、@TomLordは、私が実際に実現しなかったと思う私は(私は認めなければならないが、私はすべてのこと投稿を覚えていない:D)は、その提案を投稿するとき
ニールス


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)*""

各範囲から1文字。

また、大文字、小文字、数字がそれぞれ1つずつあるランダムなパスワードを作成するなど、各範囲から少なくとも1文字が必要な場合は、次のようにします。

( ('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は巨大ですが、データベース内の1200程度のコードのうち、かなりの数の重複がありました。それらがお互いの時間境界で起こっていることに気づきました。言い換えれば、12:12:23と2:12:22で重複が表示されるか、そのようなものです...時間が問題であるかどうかはわかりません。

このコードは、ActiveRecordオブジェクトの作成前にありました。レコードが作成される前に、このコードが実行され、「一意の」コードが生成されます。DBのエントリは常に確実に作成されましたが、コード(str上記の行)は頻繁に複製されていました。

上記の行を100,000回繰り返し実行するスクリプトを少し遅延して作成したため、1時間ごとにある種の繰り返しパターンを確認するには3〜4時間かかりますが、何も表示されませんでした。これが私のRailsアプリでなぜ起こっていたのか、私にはわかりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.