Comment générer une chaîne aléatoire dans Ruby
Question
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 clair et cela ne peut pas être passé comme argument puisqu'il ne s'agit pas d'une seule instruction.Pour obtenir une chaîne à casse mixte "a" .."z" plus "A" .."Z", je l'ai changé en :
value = ""; 8.times{value << ((rand(2)==1?65:97) + rand(25)).chr}
mais ça ressemble à une poubelle.
Quelqu'un a-t-il une meilleure méthode ?
La solution
(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 qui gaspille moins de cycles:
o = [('a'..'z'), ('A'..'Z')].map(&:to_a).flatten
string = (0...50).map { o[rand(o.length)] }.join
Autres conseils
Pourquoi ne pas utiliser SecureRandom?
require 'securerandom'
random_string = SecureRandom.hex
# outputs: 5b5cd0da3121fc53b4bc84d0c8af2e81 (i.e. 32 chars of 0..9, a..f)
SecureRandom a également des méthodes pour:
- base64
- random_bytes
- numéro_aléatoire
voir: http: // ruby-doc. org / stdlib-1.9.2 / libdoc / securerandom / rdoc / SecureRandom.html
Je l'utilise pour générer des chaînes aléatoires adaptées aux URL 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.
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
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, & # 8220; - & # 8221; et & # 8220; _ & # 8221 ;. & # 8220; = & # 8221; est également utilisé si le remplissage est vrai.
[*('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
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 et sécurisée, ce qui pourrait également être un avantage.
Modifier:Un benchmark pour le 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)
Alors le rand
la solution ne prend qu'environ 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'opterais toujours pour l'implémentation la plus sécurisée (car elle est également plus facile à appeler et plus explicite).
Je ne me souviens pas 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
require 'securerandom'
SecureRandom.urlsafe_base64(9)
Si vous voulez une chaîne de longueur spécifiée, utilisez:
require 'securerandom'
randomstring = SecureRandom.hex(n)
Il générera une chaîne aléatoire de longueur 2n
contenant 0-9
et a-f
Array.new(n){[*"0".."9"].sample}.join
,
où n = 8 dans votre cas.
Généralisé: Array.new(n){[*"A".."Z", *"0".."9"].sample}.join
, etc. - de cette réponse
require 'sha1'
srand
seed = "--#{rand(10000)}--#{Time.now}--"
Digest::SHA1.hexdigest(seed)[0,8]
Voici un code simple à une 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 de longueur 8
random_password = ('0'..'z').to_a.shuffle.first(8).join
J'espère que ça va aider et incroyable.
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"
Voici un code simple pour mot de passe aléatoire avec lenth 8
rand_password=('0'..'z').to_a.shuffle.first(8).join
J'espère que cela aidera.
Attention: 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
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]
SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
Quelque chose de Devise
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 caractères vraiment impressionnant:
characters = (32..126).to_a.pack('U*').chars.to_a
Ajoutez simplement mes centimes ici ...
def random_string(length = 8)
rand(32**length).to_s(32)
end
vous pouvez utiliser String#random
à partir des facettes de Ruby Gem facets
:
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
Mon favori est (:A..:Z).to_a.shuffle[0,8].join
. Notez que shuffle nécessite Ruby & Gt; 1.9.
Cette solution nécessite une dépendance externe, mais semble plus jolie qu'une autre.
- Installer un bijou faker
-
Faker::Lorem.characters(10) # => "ang9cbhoa8"
Donné:
chars = [*('a'..'z'),*('0'..'9')].flatten
Une seule expression, pouvant être passée en argument, autorise les caractères en double:
Array.new(len) { chars.sample }.join
Je faisais quelque chose comme ça 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)] }
Ce qui est étrange, c’est que j’ai reçu un bon nombre de doublons. Maintenant, au hasard, cela ne devrait pratiquement jamais arriver. 62 ^ 8 est énorme, mais sur environ 1200 codes de la base de données, j'ai eu un bon nombre de doublons. J'ai remarqué qu'ils se passaient à la limite d'une heure. En d'autres termes, je pourrais voir un duple à 12:12:23 et 2:12:22 ou quelque chose du genre ... je ne suis pas sûr que le temps soit au rendez-vous ou non.
Ce code était dans le précédent créer 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 permettant d'exécuter 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 heure par heure, mais rien ne se voyait. Je ne sais pas pourquoi cela se passait dans mon application rails.
''.tap {|v| 4.times { v << ('a'..'z').to_a.sample} }
Mes 2 centimes:
def token(length=16)
chars = [*('A'..'Z'), *('a'..'z'), *(0..9)]
(0..length).map {chars.sample}.join
end
Je viens d'écrire une petite pierre précieuse random_token
pour générer des jetons aléatoires dans la plupart des cas d'utilisation, profitez de ~
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
J'aime mieux la réponse de Radar, jusqu'à présent, je pense. Je peaufine 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