Ruby'de rastgele bir dize nasıl oluşturulur


747

Şu anda "A" .. "Z" için 8 karakter sahte rasgele büyük harf dizesi üretiyorum:

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

ama temiz görünmüyor ve tek bir ifade olmadığı için argüman olarak aktarılamıyor. Karma bir dize "a" .. "z" artı "A" .. "Z" almak için:

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

ama çöp gibi görünüyor.

Daha iyi bir yöntemi olan var mı?


"Tek bir ifade olmadığı için argüman olarak aktarılamayacağını" neden önemsediğinizi anlamıyorum. Neden sadece bir yardımcı program veya yardımcı yöntem yapmıyorsunuz?
David J.

1
Bir kullanıcının şifresini sıfırlamak için bir yöntem olduğunu ve yeni şifre için bir argümanın olduğunu varsayalım. Rastgele bir dize geçmek istiyorum, yukarıdaki kodda bir tmp değişkenine ihtiyacım var, oysa tek ifade örneklerinde her şeyi tek bir astar olarak yapabilirim. Tabii ki burada ve orada benzer gerekiyorsa, uzun vadede bir yarar yöntemi güzel olabilir, ama bazen sadece yerinde, bir kez, bitirmek istiyorum.
Jeff

Hayır, geçici bir değişken kullanmak zorunda değilsiniz. Bunu deneyin: reset_user_password!(random_string)nerededef random_string; SecureRandom.urlsafe_base64(20) end
David J.

8 harf utanç verici derecede zayıf bir şifredir. Md5sum verildiğinde, modern bir bilgisayar şifreyi 30 saniye içinde kurtarabilir . Daha uzun bir şeye ne dersinizsecurerandom.urlsafe_base64
Albay Panik

1
Bunun neden bu kadar çok cevabı var? Bu yararlı bir soru değil, ama nasıl dikkat çektiğini merak ediyorum.
Lou

Yanıtlar:


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

Golf yapmak için çok fazla zaman harcıyorum.

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

Ve daha da kafa karıştırıcı, ancak daha esnek ve daha az döngü harcayan sonuncusu:

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

205
34 karakter ve hızlı yanan: ('a'..'z').to_a.shuffle[0,8].join. Ruby> = 1.9 ila ihtiyacınız olduğunu unutmayın shuffle.
fny

20
Kendi kitaplığınızı alacak bir sürücünüz yoksa, varolan kitaplıklardan yararlanmak tercih edilir. SecureRandomDiğer cevaplarda örnek olarak bakınız .
David J.

28
@faraz yönteminiz işlevsel olarak aynı değil, değiştirme ile rastgele değil.
michaeltwofish

35
[*('a'..'z'),*('0'..'9')].shuffle[0,8].joinhem harf hem de rakam içeren rastgele bir dize oluşturmak için.
Robin

31
randdeterministik ve öngörülebilirdir. Parola oluşturmak için bunu kullanmayın! SecureRandomBunun yerine çözümlerden birini kullanın.
kalem

800

Neden SecureRandom kullanmıyorsunuz?

require 'securerandom'
random_string = SecureRandom.hex

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

SecureRandom ayrıca aşağıdakiler için yöntemlere sahiptir:

  • base64
  • random_bytes
  • RANDOM_NUMBER

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


11
base64 olurdu, ancak örneğindeki gibi hex değil
Jeff Dickey

67
Bu arada, 1.9 ve son 1.8 sürümlerinde stdlib'in bir parçası, bu yüzden sadece require 'securerandom'bu temiz SecureRandomyardımcıyı almak için :)
J -_- L

21
BTW SecureRandom sürüm 3.2'de ActiveSupport'tan kaldırıldı. Changelog'dan: "Standart kütüphaneden SecureRandom lehine ActiveSupport :: SecureRandom kaldırıldı".
Marc

15
SecureRandom.random_number(36**12).to_s(36).rjust(12, "0")her zaman 12 karakter uzunluğunda 0-9a-z (36 karakter) bir dize oluşturur. 12'yi istediğiniz uzunlukta değiştirin. Ne yazık ki sadece AZ kullanarak almak için hiçbir yolu Integer#to_s.
Gerry Shaw

1
@ stringo0 bu yanlış. +fGH1Bir URL'den geçmek istiyorsanız, URL'den geçen HERHANGİ bir değerde olduğu gibi URL'yi kodlamanız %2BfGH1
yeterlidir

247

Ben garanti maksimum uzunluğu ile rastgele URL dostu dizeleri oluşturmak için kullanın:

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

Küçük az ve 0-9 arası rastgele dizeler oluşturur. Çok özelleştirilebilir değil ama kısa ve temiz.


4
En kısa sürüm için +1 (harici ikili dosyaları çağırmaz ^^). Rastgele dize halka açık değilse, bazen sadece kullanıyorum rand.to_s; çirkin, ama çalışıyor.
Jo Liss

12
Bu harika bir çözüm (ve hızlı da), ancak zaman zaman ~ 40 length
Brian E

7
@Brian E bunun istediğiniz basamak güvence altına alacak: (36**(length-1) + rand(36**length)).to_s(36). 36 tabanına dönüştürülen 36 ** (uzunluk-1) 10 ** (uzunluk-1) olup, istediğiniz basamak uzunluğuna sahip en küçük değerdir.
Eric Hu

11
İşte her zaman istenen uzunlukta jeton üreten sürüm:(36**(length-1) + rand(36**length - 36**(length-1))).to_s(36)
Adrien Jarthon

3
Bu, Rails 4 ve Ruby 2.1.1'de benim için bir hata NameError: undefined local variable or method çıkarıyor: main için length ': Object`
kakubei

175

Bu çözüm, etkinleştirme kodları için kolayca okunabilen bir karakter dizisi oluşturur; İnsanların 8'i B, 1'i I, 0'ı O, L'yi 1 vb. İle karıştırmak istemedim.

# 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' belirsiz mi yoksa bu bir yazım hatası mı?
gtd

10
@gtd - Evet. U ve V ambigvov'lardır.
colinm

1
@ colinm V orada olsa.
gtd

8
Ayrıca kullanmak isteyeyim güvenli olmak SecureRandom.random_number(charset.size)yerinerand(charset.size)
GTD

1
Sadece küçük harf ve / veya bazı özel karakterler (shift + num) ekleyerek bunu geliştirmeye çalışıyordum ve aşağıdaki listeleri aldım: %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 ! @ # $ % ^ & * +}ve %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 ! @ # $ % ^ & * }(ilk liste küçük harf içermiyor, ancak daha uzun) üstesinden geldi.
dkniffin

130

Diğerleri de benzer bir şeyden bahsetti, ancak bu URL güvenli işlevini kullanıyor.

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

Sonuç AZ, az, 0-9, “-” ve “_” içerebilir. Dolgu doğru olduğunda “=” da kullanılır.


Tam da aradığım şey buydu. Teşekkürler!
Dan Williams

69

Ruby 2.5'den bu yana, gerçekten çok kolay SecureRandom.alphanumeric:

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

AZ, az ve 0-9 içeren rasgele dizeler oluşturur ve bu nedenle çoğu kullanım durumunda uygulanabilir olmalıdır. Ve rastgele bir şekilde üretilirler, bu da bir fayda olabilir.


Bu, en yüksek oyu alan çözümle karşılaştırmak için bir ölçüttür:

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)

Böylece randçözüm sadece yaklaşık 3/4 zaman alır SecureRandom. Çok sayıda dizgi oluşturursanız bu önemli olabilir, ancak zaman zaman rastgele bir dize oluşturursanız, her zaman daha güvenli bir uygulama ile giderim çünkü daha kolay ve daha açıktır.


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

Rastgele 8 harfli bir dize oluşturun (ör. NVAYXHGR)

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

Rastgele 8 karakterli bir dize (örn. 3PH4SWF2) oluşturun, 0/1 / I / O hariçtir. Yakut 1.9


4
Tek sorun, sonuçtaki her karakterin benzersiz olmasıdır. Olası değerleri sınırlar.
tybro0103

1
Bu özellik isteği gerçekleşirse, Ruby 1.9.x, değiştirmeden örnekleme için # örnek ve değiştirilmeden örnekleme için # seçenek ile sonuçlanabilir.
David J.

Bu bir hatadır, bence ihtiyacınız var ... [*("A".."Z")]'; ((single qoutes değil))
jayunit100

Bana bunu geçmek stubiçin nasıl yapabileceğimi söyleyebilir misin rspec?
Sinscary

31

Bunu nerede bulduğumu hatırlayamıyorum, ama benim için en iyi ve en az süreç yoğun görünüyor:

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


Bundan 0 ve 1 eksik değil mi?
sunnyrjuneja

Görünüşe göre bulduğum yerde olabilir.
Travis Reeder

Ve ya, 0 ve 1 eksik gibi görünüyor.
Travis Reeder

4
0Ve 1ve Ove Ibu karakterler belirsiz olduğu için kasten eksik. Bu tür bir kod, kullanıcının kopyalaması gereken bir dizi karakter oluşturmak için kullanılıyorsa, bağlamdan ayırt edilmesi zor olabilecek karakterleri hariç tutmak en iyisidir.
Andrew

28
require 'securerandom'
SecureRandom.urlsafe_base64(9)

SecureRandom mükemmel bir lib. Orada olduğunu bilmiyordum. Teşekkürler.
Dogweather

old skool (ve Random.new.bytes(9)
uglier

4
btw, urlsafe_base64 belirtilen uzunluğu yaklaşık 4/3 döndürür. Tam olarak n karakter uzunluğunda bir dize almak için deneyinn=9 ; SecureRandom.urlsafe_base64(n)[0..n-1]
tardate

25

Belirtilen uzunlukta bir dize istiyorsanız şunu kullanın:

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

Ve 2niçeren rastgele bir uzunluk dizesi oluşturur0-9a-f


Bir uzunluk dizesi oluşturmaz n, aslında 4/3 olan bir dize üretir n.
Omar Ali

@OmarAli yanılıyorsun. Ruby belgelerine göre .hex2n olması durumunda . Belgeler: SecureRandom.hex rastgele bir onaltılık dize oluşturur. N argümanı oluşturulacak rastgele sayının uzunluğunu bayt cinsinden belirtir. Elde edilen onaltılık dizenin uzunluğu n'nin iki katıdır .
Rihards


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

İlginç, ama biraz daha hesaplama açısından pahalı
Jeff

Ayrıca sınırlı karakter kapsamı için kapasite yok.
Kent Fredric

3
Onaltılık bir özetin yalnızca 0-9 ve af karakterleri döndürdüğünü unutmayın.
webmat

11

İşte uzunluğu 8 olan rastgele dize için bir satır basit kod:

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

Ayrıca uzunluğu 8 olan rastgele bir şifre için de kullanabilirsiniz:

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

2
Kendiniz bir şifre oluşturmak için bunu kullanmamalısınız, çünkü bu yöntem asla bir karakteri tekrarlamaz. Bu nedenle sadece P(36, 8) / 36^8 = 0.48 karakter için olası karakter alanını (kaba kuvvet uygulamak için ~ 2x daha kolay) veya P(36, 25) / 36^25 = 0.0000125 karakter için olası karakter alanını (kaba kuvvet için ~ 100.000x daha kolay) kullanırsınız.
Buzullar

10

Yakut 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Çözelti gerçekten güzel!
Misha Moroshko

#sampleKaç eleman istediğinizi sorabilirsiniz . Örneğin ALPHABET.sample(10).join... ruby-doc.org/core-2.4.0/Array.html#method-i-sample
odlp

Bu aslında yanlış. sample(10)size 10 benzersiz örnek verir. Ama tekrar etmelerine izin vermek istiyorsun. Ama Array.new(10).mapperformans için kullanırım
Saša Zejnilović

Hem alt hem de üst vakalarda alfasayısal istedim. Ayrıca kullanmaya Array.newve blok sözdizimine geçtim . Array.new(20) { [*'0'..'9', *'a'..'z', *'A'..'Z'].sample }.join
taylorthurlow

8

Dikkat: randbir saldırgan için tahmin edilebilir ve bu nedenle muhtemelen güvensizdir. Şifre oluşturmak için kesinlikle SecureRandom kullanmalısınız. Böyle bir şey kullanıyorum:

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

Bu muhtemelen "en" güvenli çözümdür. SecureRandom, işletim sistemi tarafından sağlanan temel güvenlik API'lerini kullanmaya çalışır. OpenSSL'iniz varsa bunu kullanacak, eğer Windows'daysanız oradaki en iyi seçenek olacak. Özellikle bu çözümü seviyorum çünkü kullanım için bir dizi karakter belirtmenize izin veriyor. Karakter kümeniz bir baytın maksimum değerinden daha uzunsa işe yaramasa da: 255. Dokümanda SecureRandom için kaynak kodunu görüntülemenizi öneririz: ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/ rdoc /…
Breedly

8

İşte uzunluğu 8 olan rastgele şifre için basit bir kod:

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

7

Kullanmak istediğim başka bir yöntem:

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

ljustDoğru dize uzunluğu hakkında gerçekten paranoyaysanız ekleyin :

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

İpin sağ tarafını kullanarak rasgele sayının en az önemli kısmını almak daha da iyidir: rand (2 ** 64) .to_s (36) [- 10,10]
Jeff

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

Devise'den bir şey


Dize karakterlerini neden kullanıyor .tr('+/=lIO0', 'pqrsxyz')?
miguelcobain

Özel karakterler URL güvenli olmadığı için. Ve l / I veya O / 0 çünkü okunabilir kullanıcı parolaları oluşturmak için tekniği kullanırsanız çok kolay karışırlar.
ToniTornado

Bu işlevin belirli karakterlere karşı biraz önyargısı vardır. Ayrıca diğer uzunluklar için (örneğin 16), son karakter rastgele olmayacaktır. İşte bundan kaçınmanın bir yolu. SecureRandom.base64 (64) .tr ('+ / = lIO01', '') [0,16]
Shai Coleman

5

Bence bu hoşluk, açıklık ve modifikasyon kolaylığı arasında güzel bir denge.

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

Kolayca değiştirilebilir

Örneğin, rakamlar dahil:

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

Büyük harf onaltılık:

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

Gerçekten etkileyici bir karakter dizisi için:

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

1
Ben sadece büyük harfler + sayılar kullanmak için tavsiye ederim, ayrıca "kafa karıştırıcı" olanları kaldırmak charset = (1..9) .to_a.concat (('A' .. 'Z'). to_a) .reject {| a | [0, 1, 'O', 'I']. İçerir? (A)} (0 ... boyut) .map {charset [rand (charset.size)]}. Katıl
parıltı

5

Sadece sentlerimi buraya ekliyorum ...

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

3
Not: bu her zaman tam olarak + uzunluk + uzun bir dize döndürmez - daha kısa olabilir. Bu, tarafından döndürülen sayıya bağlıdırrand
tardate

5

String#randomRuby Geminin Şeylerinden kullanabilirsiniz facets.

Temel olarak bunu yapar:

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

Benim favorim (:A..:Z).to_a.shuffle[0,8].join. Karıştırma işleminin Ruby> 1.9 gerektirdiğini unutmayın.


4

Bu çözümün dışa bağımlı olması gerekiyor, ancak diğerinden daha güzel görünüyor.

  1. Gem faker yükleyin
  2. Faker::Lorem.characters(10) # => "ang9cbhoa8"

4

Verilen:

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

Tek bir ifade, bağımsız değişken olarak geçirilebilir, yinelenen karakterlere izin verir:

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

3

Radar'ın cevabını en çok seviyorum, şimdiye kadar. Bu şekilde biraz değiştiririm:

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

Neden kullanmıyorsunuz (CHARS*length).sample(length).join?
niels

@niels Bu öneri , tekrarlanmayan karakterler lehine ağırlıklı bir dize oluşturur . Örneğin, eğer CHARS=['a','b']o zaman yöntem üretecek "aa"veya "bb"zamanın sadece% 33, ancak "ab"ya "ba"zamanın% 67. Belki bu bir sorun değil, ama akılda tutmaya değer!
Tom Lord

good point, @TomLord, sanırım bu öneriyi gönderdiğimde aslında bunu fark etmedim (itiraf etmeliyim de bunu gönderdiğimi hatırlamıyorum: D)
niels

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

3

2 sentim:

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


3

3 aralıktan oluşan rastgele bir dize için 2 çözüm:

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

Her Aralıktan Bir Karakter.

Ve her aralıktan bir büyük harf, bir küçük harf ve bir rakam içeren rastgele bir şifre oluşturmak gibi en az bir karaktere ihtiyacınız varsa, şöyle bir şey yapabilirsiniz:

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

Ve her aralığın belirli bir sayısını uygulamak istiyorsanız, böyle bir şey yapabilirsiniz:( ('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

Son zamanlarda böyle bir şey yapıyordum 62 karakterden 8 bayt rastgele dize üretmek için. Karakterler 0-9, az, AZ idi. Ben 8 kez döngü ve diziden rastgele bir değer toplama gibi onları bir dizi vardı. Bu bir Rails uygulamasının içindeydi.

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

Tuhaf olan, çok sayıda kopyamın olması. Şimdi rastgele bu neredeyse hiç olmamalı. 62 ^ 8 çok büyük, ama db 1200 veya kodları dışında çoğaltmaları iyi bir sayı vardı. Birbirlerinin saat sınırlarında olduklarını fark ettim. Başka bir deyişle, 12:12:23 ve 2:12:22 saatlerinde bir çift görebilirim ya da bunun gibi bir şey ... zamanın sorun olup olmadığından emin değilim.

Bu kod, bir ActiveRecord nesnesinin oluşturulmasından önce idi. Kayıt oluşturulmadan önce bu kod çalıştırılır ve 'benzersiz' kod oluşturulur. DB'deki girişler her zaman güvenilir bir şekilde üretildi, ancak kod ( stryukarıdaki satırda) çok sık kopyalandı.

Bu satırın 100000 yinelemesini küçük bir gecikmeyle çalıştırmak için bir komut dosyası oluşturdum, böylece saatte bir tür tekrarlama desenini görmeyi umarak 3-4 saat sürecek, ancak hiçbir şey göremedim. Bunun neden Rails uygulamamda olduğunu bilmiyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.