I'm currently generating an 8-character pseudo-random uppercase string for "A" .. "Z":
value = ""; 8.times{value << (65 + rand(25)).chr}
but it doesn't look clean, and it can't be passed as an argument since it isn't a single statement. To get a mixed-case string "a" .. "z" plus "A" .. "Z", I changed it to:
value = ""; 8.times{value << ((rand(2)==1?65:97) + rand(25)).chr}
but it looks like trash.
Does anyone have a better method?
(0...8).map { (65 + rand(26)).chr }.join
I spend too much time golfing.
(0...50).map { ('a'..'z').to_a[rand(26)] }.join
And a last one that's even more confusing, but more flexible and wastes fewer cycles:
o = [('a'..'z'), ('A'..'Z')].map(&:to_a).flatten
string = (0...50).map { o[rand(o.length)] }.join
Why not use SecureRandom?
require 'securerandom'
random_string = SecureRandom.hex
# outputs: 5b5cd0da3121fc53b4bc84d0c8af2e81 (i.e. 32 chars of 0..9, a..f)
SecureRandom also has methods for:
see: http://ruby-doc.org/stdlib-1.9.2/libdoc/securerandom/rdoc/SecureRandom.html
I use this for generating random URL friendly strings with a guaranteed maximum length:
string_length = 8
rand(36**string_length).to_s(36)
It generates random strings of lowercase a-z and 0-9. It's not very customizable but it's short and clean.
This solution generates a string of easily readable characters for activation codes; I didn't want people confusing 8 with B, 1 with I, 0 with O, L with 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
Others have mentioned something similar, but this uses the URL safe function.
require 'securerandom'
p SecureRandom.urlsafe_base64(5) #=> "UtM7aa8"
p SecureRandom.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
p SecureRandom.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
The result may contain A-Z, a-z, 0-9, “-” and “_”. “=” is also used if padding is true.
Since Ruby 2.5, it's really easy with SecureRandom.alphanumeric
:
len = 8
SecureRandom.alphanumeric(len)
=> "larHSsgL"
It generates random strings containing A-Z, a-z and 0-9 and therefore should be applicable in most use-cases. And they are generated randomly secure, which might be a benefit, too.
This is a benchmark to compare it with the solution having the most upvotes:
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)
So the rand
solution only takes about 3/4 of the time of SecureRandom
. That might matter if you generate a lot of strings, but if you just create some random string from time to time I'd always go with the more secure implementation since it is also easier to call and more explicit.
[*('A'..'Z')].sample(8).join
Generate a random 8 letter string (e.g. NVAYXHGR)
([*('A'..'Z'),*('0'..'9')]-%w(0 1 I O)).sample(8).join
Generate a random 8 character string (e.g. 3PH4SWF2), excludes 0/1/I/O. Ruby 1.9
I can't remember where I found this, but it seems like the best and the least process intensive to me:
def random_string(length=10)
chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789'
password = ''
length.times { password << chars[rand(chars.size)] }
password
end
require 'securerandom'
SecureRandom.urlsafe_base64(9)
If you want a string of specified length, use:
require 'securerandom'
randomstring = SecureRandom.hex(n)
It will generate a random string of length 2n
containing 0-9
and a-f
Array.new(n){[*"0".."9"].sample}.join
,
where n=8
in your case.
Generalized: Array.new(n){[*"A".."Z", *"0".."9"].sample}.join
, etc.
Here is one line simple code for random string with length 8:
random_string = ('0'..'z').to_a.shuffle.first(8).join
You can also use it for random password having length 8:
random_password = ('0'..'z').to_a.shuffle.first(8).join
require 'sha1'
srand
seed = "--#{rand(10000)}--#{Time.now}--"
Digest::SHA1.hexdigest(seed)[0,8]
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"
Here is one simple code for random password with length 8:
rand_password=('0'..'z').to_a.shuffle.first(8).join
Be aware: rand
is predictable for an attacker and therefore probably insecure. You should definitely use SecureRandom if this is for generating passwords. I use something like this:
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
Another method I like to use:
rand(2**256).to_s(36)[0..7]
Add ljust
if you are really paranoid about the correct string length:
rand(2**256).to_s(36).ljust(8,'a')[0..7]
SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
Something from Devise
You can use String#random
from the Facets of Ruby Gem facets
.
It basically does this:
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
Just adding my cents here...
def random_string(length = 8)
rand(32**length).to_s(32)
end
I think this is a nice balance of conciseness, clarity and ease of modification.
characters = ('a'..'z').to_a + ('A'..'Z').to_a
# Prior to 1.9, use .choice, not .sample
(0..8).map{characters.sample}.join
For example, including digits:
characters = ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a
Uppercase hexadecimal:
characters = ('A'..'F').to_a + (0..9).to_a
For a truly impressive array of characters:
characters = (32..126).to_a.pack('U*').chars.to_a
My favorite is (:A..:Z).to_a.shuffle[0,8].join
. Note that shuffle requires Ruby > 1.9.
This solution needs external dependency, but seems prettier than another.
Faker::Lorem.characters(10) # => "ang9cbhoa8"
Given:
chars = [*('a'..'z'),*('0'..'9')].flatten
Single expression, can be passed as an argument, allows duplicate characters:
Array.new(len) { chars.sample }.join
I like Radar's answer best, so far, I think. I'd tweak a bit like this:
CHARS = ('a'..'z').to_a + ('A'..'Z').to_a
def rand_string(length=8)
s=''
length.times{ s << CHARS[rand(CHARS.length)] }
s
end
My 2 cents:
def token(length=16)
chars = [*('A'..'Z'), *('a'..'z'), *(0..9)]
(0..length).map {chars.sample}.join
end
I like Radar's answer best, so far, I think. I'd tweak a bit like this:
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 solutions for a random string consisting of 3 ranges:
(('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)*""
And if you need at least one character from each range, such as creating a random password that has one uppercase, one lowercase letter and one digit, you can do something like this:
( ('a'..'z').to_a.sample(8) + ('A'..'Z').to_a.sample(8) + (0..9).to_a.sample(8) ).shuffle.join
#=> "Kc5zOGtM0H796QgPp8u2Sxo1"
I just write a small gem random_token
to generate random tokens for most use case, enjoy ~
I was doing something like this recently to generate an 8 byte random string from 62 characters. The characters were 0-9,a-z,A-Z. I had an array of them as was looping 8 times and picking a random value out of the array. This was inside a Rails app.
str = ''
8.times {|i| str << ARRAY_OF_POSSIBLE_VALUES[rand(SIZE_OF_ARRAY_OF_POSSIBLE_VALUES)] }
The weird thing is that I got good number of duplicates. Now randomly this should pretty much never happen. 62^8 is huge, but out of 1200 or so codes in the db i had a good number of duplicates. I noticed them happening on hour boundaries of each other. In other words I might see a duple at 12:12:23 and 2:12:22 or something like that...not sure if time is the issue or not.
This code was in the before create of an ActiveRecord object. Before the record was created this code would run and generate the 'unique' code. Entries in the DB were always produced reliably, but the code (str
in the above line) was being duplicated much too often.
I created a script to run through 100000 iterations of this above line with small delay so it would take 3-4 hours hoping to see some kind of repeat pattern on an hourly basis, but saw nothing. I have no idea why this was happening in my Rails app.
''.tap {|v| 4.times { v << ('a'..'z').to_a.sample} }
With this method you can pass in an abitrary length. It's set as a default as 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
Another trick that works with Ruby 1.8+ and is fast is:
>> require "openssl"
>> OpenSSL::Random.random_bytes(20).unpack('H*').join
=> "2f3ff53dd712ba2303a573d9f9a8c1dbc1942d28"
It get's you random hex string. Similar way you should be able to generate base64 string ('M*').
With this method you can pass in an abitrary length. It's set as a default as 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
Here is another method:
Needs 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
try this out
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
I love the answers of the thread, have been very helpful, indeed!, but if I may say, none of them satisfies my ayes, maybe is the rand() method. it's just doesn't seems right to me, since we've got the Array#choice method for that matter.
If you are on a UNIX and you still must use Ruby 1.8 (no SecureRandom) without Rails, you can also use this:
random_string = `openssl rand -base64 24`
Note this spawns new shell, this is very slow and it can only be recommended for scripts.
Here is a improve of @Travis R answer:
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
At @Travis R answer chars and numbers were together, so sometimes random_string
could return only numbers or only characters. With this improve at least half of random_string
will be characters and the rest are numbers. Just in case if you need a random string with numbers and characters
For devise secure_validatable you can use this
(0...8).map { ([65, 97].sample + rand(26)).chr }.push(rand(99)).join
We've been using this on our code:
class String
def self.random(length=10)
('a'..'z').sort_by {rand}[0,length].join
end
end
The maximum length supported is 25 (we're only using it with the default anyway, so hasn't been a problem).
Someone mentioned that 'a'..'z' is suboptimal if you want to completely avoid generating offensive words. One of the ideas we had was removing vowels, but you still end up with WTFBBQ etc.
10.times do
alphabet = ('a'..'z').to_a
string += alpha[rand(alpha.length)]
end
This is based on a few other answers, but it adds a bit more complexity:
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
Essentially it ensures a password that is 8 - 20 characters in length, and which contains at least one number and one special character.
Use 'SafeRandom' Gem GithubLink
It will provide the easiest way to generate random values for Rails2, Rails 3, Rails 4, Rails 5 compatible.
a='';8.times{a<<[*'a'..'z'].sample};p a
or
8.times.collect{[*'a'..'z'].sample}.join
To make your first into one statement:
(0...8).collect { |n| value << (65 + rand(25)).chr }.join()
Here's a solution that is flexible and allows dups:
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
Example:
"ATCG".random(8) => "CGTGAAGA"
You can also allow a certain character to appear more frequently:
"AAAAATCG".random(10) => "CTGAAAAAGC"
Explanation: The method above takes the chars of a given string and generates a big enough array. It then shuffles it, takes the first n items, then joins them.
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
To make your first into one statement:
(0...8).collect { |n| value << (65 + rand(25)).chr }.join()
Create an empty string or a pre-fix if require:
myStr = "OID-"
Use this code to populate the string with random numbers:
begin; n = ((rand * 43) + 47).ceil; myStr << n.chr if !(58..64).include?(n); end while(myStr.length < 12)
Notes:
(rand * 43) + 47).ceil
It will generate random numbers from 48-91 (0,1,2..Y,Z)
!(58..64).include?(n)
It is used to skip special characters (as I am not interested to include them)
while(myStr.length < 12)
It will generate total 12 characters long string including prefix.
Sample Output:
"OID-XZ2J32XM"
`pwgen 8 1`.chomp
In ruby 1.9 one can use Array's choice method which returns random element from array
This is almost as ugly but perhaps as step in right direction?
(1..8).map{|i| ('a'..'z').to_a[rand(26)]}.join
I don't know ruby, so I can't give you the exact syntax, but I would set a constant string with the list of acceptable characters, then use the substring operator to pick a random character out of it.
The advantage here is that if the string is supposed to be user-enterable, then you can exclude easily confused characters like l and 1 and i, 0 and O, 5 and S, etc.
Source: Stackoverflow.com