web-dev-qa-db-fra.com

Comment générer une chaîne aléatoire en Ruby

Je génère actuellement une chaîne majuscule pseudo-aléatoire de 8 caractères pour "A" .. "Z":

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

mais cela n'a pas l'air propre et il ne peut pas être passé comme argument puisqu'il ne s'agit pas d'une seule déclaration. Pour obtenir une chaîne de casse mixte "a" .. "z" plus "A" .. "Z", je l'ai changée en:

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

mais cela ressemble à une corbeille.

Est-ce que quelqu'un a une meilleure méthode?

702
Jeff
(0...8).map { (65 + Rand(26)).chr }.join

Je passe trop de temps à jouer au golf.

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

Et un dernier qui est encore plus déroutant, mais plus flexible et gaspille moins de cycles:

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

Pourquoi ne pas utiliser SecureRandom?

require 'securerandom'
random_string = SecureRandom.hex

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

SecureRandom dispose également de méthodes pour:

  • base64
  • random_bytes
  • nombre_aléatoire

voir: http://Ruby-doc.org/stdlib-1.9.2/libdoc/securerandom/rdoc/SecureRandom.html

754
christopherstyles

J'utilise ceci pour générer des chaînes amicales d'URL aléatoires avec une longueur maximale garantie:

Rand(36**length).to_s(36)

Il génère des chaînes aléatoires de minuscules a-z et 0-9. Ce n'est pas très personnalisable mais c'est court et propre.

236
Christoffer Möller

Cette solution génère une chaîne de caractères facilement lisibles pour les codes d'activation. Je ne voulais pas que les gens confondent 8 avec B, 1 avec I, 0 avec O, L avec 1, etc.

# 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
165
ImNotQuiteJack

D'autres ont mentionné quelque chose de similaire, mais cela utilise la fonction URL safe.

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

Le résultat peut contenir A-Z, a-z, 0-9, "-" et "_". “=” Est également utilisé si le remplissage est vrai.

124
Travis Reeder
[*('A'..'Z')].sample(8).join

Générer une chaîne aléatoire de 8 lettres (par exemple, NVAYXHGR)

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

Génère une chaîne aléatoire de 8 caractères (par exemple 3PH4SWF2), à l'exclusion de 0/1/I/O. Ruby 1.9

45
Shai Coleman

Depuis Ruby 2.5 vraiment facile avec SecureRandom.alphanumeric:

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

Génère des chaînes aléatoires contenant A-Z, a-z et 0-9 et devrait donc être applicable dans la plupart des cas d'utilisation. Et ils sont générés de manière aléatoire, ce qui pourrait également être un avantage.


Edit: Un benchmark à comparer avec la solution ayant le plus de votes positifs:

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)

Donc, la solution Rand ne prend que 3/4 du temps de SecureRandom. Cela peut avoir de l'importance si vous générez beaucoup de chaînes, mais si vous créez simplement une chaîne aléatoire de temps en temps, j'utiliserais toujours une implémentation plus sécurisée (car elle est également plus facile à appeler et plus explicite).

34
Markus

Je ne me souviens plus où j'ai trouvé cela, mais cela me semble être le meilleur et le moins intensif en processus:

def random_string(length=10)
  chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789'
  password = ''
  length.times { password << chars[Rand(chars.size)] }
  password
end
30
Travis Reeder
require 'securerandom'
SecureRandom.urlsafe_base64(9)
27
LENZCOM

Si vous voulez une chaîne de longueur spécifiée, utilisez:

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

Il va générer une chaîne aléatoire de longueur 2n contenant 0-9 et a-f

24
Srikanta Mahapatro

Array.new(n){[*"0".."9"].sample}.join, where n = 8 dans votre cas.

Généralisé: Array.new(n){[*"A".."Z", *"0".."9"].sample}.join, etc. - from this answer

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

Voici un code simple en ligne pour une chaîne aléatoire de longueur 8

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

Vous pouvez également l'utiliser pour un mot de passe aléatoire d'une longueur de 8.

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

j'espère que ça va aider et incroyable.

10
Awais

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"
10
Ragmaanir

Sachez que Rand est prévisible pour un attaquant et donc probablement non sécurisé. Vous devez absolument utiliser SecureRandom s’il s’agit de générer des mots de passe. J'utilise quelque chose comme ça:

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
8
pencil

Voici un code simple pour mot de passe aléatoire de longueur 8

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

J'espère que ça va aider.

8
Thaha kp
SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')

Quelque chose de Devise

6
Thorpe Obazee

Il suffit d'ajouter mes cents ici ...

def random_string(length = 8)
  Rand(32**length).to_s(32)
end
5
pduersteler

vous pouvez utiliser String#random à partir des facettes de Ruby Gem facets:

https://github.com/rubyworks/facets/blob/126a619fd766bc45588cac18d09c4f1927538e33/lib/core/facets/string/random.rb

il fait essentiellement ceci:

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
5
Tilo

Une autre méthode que j'aime utiliser

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

Ajoutez ljust si vous êtes vraiment paranoïaque à propos de la longueur de chaîne correcte:

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

Je pense que c’est un bon équilibre entre concision, clarté et facilité de modification. 

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

Facilement modifié

Par exemple, y compris les chiffres:

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

Hexadécimal majuscule:

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

Pour un nombre de personnages vraiment impressionnant:

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

Mon préféré est (:A..:Z).to_a.shuffle[0,8].join. Notez que Shuffle requiert Ruby> 1.9.

4
Josh

Donné:

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

Une seule expression, pouvant être passée en argument, permet les doublons de caractères:

Array.new(len) { chars.sample }.join
4
Tim James

Mes 2 centimes:

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

Je viens d'écrire un petit joyau random_token pour générer des jetons aléatoires pour la plupart des cas d'utilisation, profitez ~

https://github.com/sibevin/random_token

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

Je faisais quelque chose comme ceci récemment pour générer une chaîne aléatoire de 8 octets à partir de 62 caractères. Les caractères étaient 0-9, a-z, A-Z. J'avais un tableau d'entre eux en boucle 8 fois et en sélectionnant une valeur aléatoire dans le tableau. C'était à l'intérieur d'une application Rails.

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

La chose étrange est que j'ai eu bon nombre de doublons. Maintenant, au hasard, cela ne devrait pratiquement jamais arriver. 62 ^ 8 est énorme, mais sur 1 200 environ, les codes de la base de données comportaient un bon nombre de doublons. J'ai remarqué qu'ils se passaient à des limites horaires les unes des autres. En d'autres termes, je pourrais voir un duple à 12:12:23 et 2:12:22 ou quelque chose du genre ... je ne sais pas si le temps est compté ou pas.

Ce code était dans le précédent créer d'un objet activerecord. Avant la création de l'enregistrement, ce code était exécuté et générait le code "unique". Les entrées dans la base de données étaient toujours produites de manière fiable, mais le code (str dans la ligne ci-dessus) était dupliqué beaucoup trop souvent.

J'ai créé un script pour parcourir 100 000 itérations de cette ligne ci-dessus avec un léger retard, de sorte que cela prendrait 3-4 heures dans l'espoir de voir une sorte de motif de répétition sur une base horaire, mais je ne voyais rien. Je ne sais pas pourquoi cela se passait dans mon application Rails.

3
erik

Si vous êtes sous UNIX et que vous devez toujours utiliser Ruby 1.8 (pas de SecureRandom) sans Rails, vous pouvez également utiliser ceci:

random_string = `openssl Rand -base64 24`

Notez que cela engendre un nouveau Shell, cela est très lent et cela ne peut être recommandé que pour les scripts.

2
lzap

Une autre astuce qui fonctionne avec Ruby 1.8+ et qui est rapide est:

>> require "openssl"
>> OpenSSL::Random.random_bytes(20).unpack('H*').join
=> "2f3ff53dd712ba2303a573d9f9a8c1dbc1942d28"

Vous obtenez une chaîne hexagonale aléatoire. De la même manière, vous devriez pouvoir générer une chaîne base64 ('M *').

2
lzap

J'aime mieux la réponse de Radar, jusqu'à présent, je pense. Je Tweak un peu comme ça:

CHARS = ('a'..'z').to_a + ('A'..'Z').to_a
def Rand_string(length=8)
  s=''
  length.times{ s << CHARS[Rand(CHARS.length)] }
  s
end
2
webmat

Avec cette méthode, vous pouvez transmettre une longueur arbitraire. Il est défini par défaut sur 6.

def generate_random_string(length=6)
  string = ""
  chars = ("A".."Z").to_a
  length.times do
    string << chars[Rand(chars.length-1)]
  end
  string
end
2
Ryan Bigg

Mon meilleur coup, 2 solutions pour une chaîne aléatoire composée de 3 plages

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

essayez ceci

def Rand_name(len=9)
  ary = [('0'..'9').to_a, ('a'..'z').to_a, ('A'..'Z').to_a]
  name = ''

  len.times do
    name << ary.choice.choice
  end
  name
end

J'aime les réponses du fil, ont été très utiles, en effet !, mais si je peux dire, aucun d'entre eux ne satisfait mes oui, peut-être la méthode Rand (). ça ne me semble tout simplement pas juste, puisque nous avons la méthode de choix Array # pour cette question.

2
Manuel A. Guilamo

Voici une autre méthode:

  • Il utilise le générateur de nombre aléatoire sécurisé au lieu de Rand ()
  • Peut être utilisé dans les URL et les noms de fichiers
  • Contient des majuscules, des minuscules et des chiffres
  • A une option pour ne pas inclure de caractères ambigus I0l01

Besoins require "securerandom"

def secure_random_string(length = 32, non_ambiguous = false)
  characters = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a

  %w{I O l 0 1}.each{ |ambiguous_character| 
    characters.delete ambiguous_character 
  } if non_ambiguous

  (0...length).map{
    characters[ActiveSupport::SecureRandom.random_number(characters.size)]
  }.join
end
2
Evgenii
10.times do 
  alphabet = ('a'..'z').to_a
  string += alpha[Rand(alpha.length)]
end
1
mminski

Ceci est basé sur quelques autres réponses, mais cela ajoute un peu plus de complexité:

def random_password
  specials = ((32..47).to_a + (58..64).to_a + (91..96).to_a + (123..126).to_a).pack('U*').chars.to_a
  numbers  = (0..9).to_a
  alpha    = ('a'..'z').to_a + ('A'..'Z').to_a
  %w{i I l L 1 O o 0}.each{ |ambiguous_character| 
    alpha.delete ambiguous_character 
  }
  characters = (alpha + specials + numbers)
  password = Random.new.Rand(8..18).times.map{characters.sample}
  password << specials.sample unless password.join =~ Regexp.new(Regexp.escape(specials.join))
  password << numbers.sample  unless password.join =~ Regexp.new(Regexp.escape(numbers.join))
  password.shuffle.join
end

Cela garantit essentiellement un mot de passe de 8 à 20 caractères, contenant au moins un chiffre et un caractère spécial.

1
Chris Bloom

Pour la clé secure_validatable, vous pouvez utiliser cette

(0 ... 8) .map {([65, 97] .sample + Rand (26)). Chr} .Push (Rand (99)). Join

1
shiva kumar

Voici une amélioration de la réponse de @Travis R:

 def random_string(length=5)
    chars = 'abdefghjkmnpqrstuvwxyzABDEFGHJKLMNPQRSTUVWXYZ'
    numbers = '0123456789'
    random_s = ''
    (length/2).times { random_s << numbers[Rand(numbers.size)] }
    (length - random_s.length).times { random_s << chars[Rand(chars.size)] }
    random_s.split('').shuffle.join
  end

Sur @Travis R, les caractères de réponse et les chiffres étaient ensemble, donc parfois random_string pouvait ne renvoyer que des nombres ou des caractères. Avec cette amélioration, au moins la moitié de random_string seront des caractères et les autres seront des nombres. Juste au cas où vous auriez besoin d'une chaîne aléatoire avec des chiffres et des caractères

0
Lucas Andrade

Pour faire votre première déclaration dans une déclaration:

(0...8).collect { |n| value  << (65 + Rand(25)).chr }.join()
0
Kevin Conner

Le moyen le plus simple est d'utiliser string_pattern gem https://github.com/MarioRuiz/string_pattern

Par exemple, pour générer 36 lettres aléatoires:

 require 'string_pattern'
 puts '36:L'.gen

Aussi, vous pouvez utiliser des expressions régulières

 require 'string_pattern'
 puts /[a-zA-Z]{36}/.gen
0
Mario Ruiz

Créez une chaîne vide ou un préfixe si besoin:

myStr = "OID-"

Utilisez ce code pour renseigner la chaîne avec des nombres aléatoires:

begin; n = ((Rand * 43) + 47).ceil; myStr << n.chr if !(58..64).include?(n); end while(myStr.length < 12)

Remarques:

(Rand * 43) + 47).ceil

Il va générer des nombres aléatoires de 48 à 91 (0,1,2..Y, Z)

!(58..64).include?(n)

Il est utilisé pour ignorer les caractères spéciaux (je ne suis pas intéressé à les inclure)

while(myStr.length < 12)

Il générera une chaîne totale de 12 caractères incluant le préfixe.

Exemple de sortie:

"OID-XZ2J32XM"
0
Ghazi
a='';8.times{a<<[*'a'..'z'].sample};p a

ou

8.times.collect{[*'a'..'z'].sample}.join
0
Michael Kingski

Nous utilisons cela sur notre code:

class String

  def self.random(length=10)
    ('a'..'z').sort_by {Rand}[0,length].join
  end

end

La longueur maximale prise en charge est de 25 (nous l'utilisons uniquement avec la valeur par défaut, donc cela n'a pas posé de problème).

Quelqu'un a mentionné que 'a' .. 'z' est sous-optimal si vous voulez éviter complètement de générer des mots offensants. L'une de nos idées était de supprimer les voyelles, mais vous vous retrouvez toujours avec WTFBBQ, etc.

0
Carlos Villela
`pwgen 8 1`.chomp
0
Nathan L Smith

Voici une solution flexible qui permet des dupes:

class String
  # generate a random string of length n using current string as the source of characters
  def random(n)
    return "" if n <= 0
    (chars * (n / length + 1)).shuffle[0..n-1].join  
  end
end

Exemple:

"ATCG".random(8) => "CGTGAAGA"

Vous pouvez également autoriser un certain caractère à apparaître plus fréquemment:

"AAAAATCG".random(10) => "CTGAAAAAGC"

Explication: La méthode ci-dessus prend les caractères d'une chaîne donnée et génère un tableau assez grand. Il le mélange ensuite, prend les n premiers éléments, puis les joint.

0
Abdo
Array.new(8).inject(""){|r|r<<('0'..'z').to_a.shuffle[0]}  # 57
(1..8).inject(""){|r|r<<('0'..'z').to_a.shuffle[0]}        # 51
e="";8.times{e<<('0'..'z').to_a.shuffle[0]};e              # 45
(1..8).map{('0'..'z').to_a.shuffle[0]}.join                # 43
(1..8).map{Rand(49..122).chr}.join                         # 34
0
Automatico

Utilisez 'SafeRandom' Gem GithubLink

Il constituera le moyen le plus simple de générer des valeurs aléatoires pour la compatibilité de Rails2, Rails 3, Rails 4 et Rails 5. 

0
Rubyist