784

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?

the Tin Man
  • 150,910
  • 39
  • 198
  • 279
Jeff
  • 6,296
  • 5
  • 25
  • 33
  • I don't understand why you care that "since it isn't a single statement it can't be passed as an argument". Why not just make it a utility or helper method? – David J. Jun 14 '12 at 18:53
  • 1
    Suppose there is a method to reset a user's password and it has an argument for the new password. I would like to pass in a random string, in the above code I need a tmp variable, whereas in the single statement examples bellow I can do the whole thing as a one liner. Sure a utility method could be nice in the long run, esp if I'm needing similar here and there, but sometimes you just want it in place, one time, done. – Jeff Jun 26 '12 at 16:07
  • No, you don't have to use a temporary variable. Try this: `reset_user_password!(random_string)` where `def random_string; SecureRandom.urlsafe_base64(20) end` – David J. Jun 26 '12 at 16:13
  • 8 letters is a shamefully weak password. Given the md5sum a modern PC could recover the password in [30 seconds](http://www.codinghorror.com/blog/2012/04/speed-hashing.html). How about something longer [`securerandom.urlsafe_base64`](http://rubydoc.info/stdlib/securerandom/1.9.2/SecureRandom#urlsafe_base64-class_method) – Colonel Panic Oct 13 '12 at 15:46
  • Lol, ok that was asked in 2008, sure lets bump that up to 15 or so. – Jeff Oct 26 '12 at 22:17
  • 1
    Why does this have so many answers? Not that it's not a useful question, but I'm curious how it has attracted attention. – Lou May 16 '14 at 19:46
  • well, put a webdesigner infront of VIM and ask him to save and exit; Jokes apart. `require 'securerandom'; SecureRandom.hex(15)` should work fine – illusionist Apr 22 '16 at 07:45

50 Answers50

1005
(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

If you want to generate some random text then use the following:

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

this code generates 50 random word string with words length less than 10 characters and then join with space

jprio
  • 17
  • 7
Kent Fredric
  • 54,014
  • 14
  • 101
  • 148
  • 214
    34 characters and blazing fast: `('a'..'z').to_a.shuffle[0,8].join`. Note you'll need Ruby >=1.9 to `shuffle`. – fny Jun 14 '12 at 17:35
  • first example should be rand(26) otherwise you never get the letter 'Z' – Alex Gaudio Jun 18 '12 at 22:17
  • 23
    Leveraging existing libraries is preferable unless you have a driver to roll your own. See `SecureRandom` as one example, in the other answers. – David J. Jun 26 '12 at 16:15
  • 28
    @faraz your method isn't functionally the same, it's not random with replacement. – michaeltwofish Jul 06 '12 at 07:13
  • Touche @michaeltwofish. Here's the shortest and most performant one-liner I could muster: `[].fill(0,l){rand(65..90).chr}.join` (35 chars). `fill` is faster than `map`, but for real performance, drop the golf and prebuild the array with a _proper_ use of `sample`: `chars=["A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];[].fill(0,l){chars.sample}.join` That not even significantly faster for 8 characters. With 8 characters, no worries. Anything flies. ;) See my [full benchmarks](http://goo.gl/cOu9G) for details. – fny Jul 07 '12 at 08:33
  • 39
    `[*('a'..'z'),*('0'..'9')].shuffle[0,8].join` to generate a random string with both letters and numbers. – Robin Mar 05 '13 at 13:50
  • 32
    `rand` is deterministic and predictable. Don't use this for generating passwords! Use one of the `SecureRandom` solutions instead. – pencil Mar 30 '13 at 14:33
  • 2
    the last example should read `(0...50).map { [('a'..'z'),('A'..'Z')].flat_map(&:to_a).sample }.join` you definitely *have not* spent _too_ much time golfing xD – Dennis Krupenik Aug 16 '13 at 13:44
  • 1
    further reduced from Dennis's answer `(0..50).map { [*'a'..'z',*'A'..'Z'].sample }.join` – Vizjerai Oct 09 '13 at 21:03
  • 2
    I've just created a module `RandomString` summarising the three main techniques mentioned in all the answers: https://gist.github.com/jopotts/8706578 – Jo P Jan 30 '14 at 11:21
  • 4
    Always use existing solutions like SecureRandom in favor of homegrown "random" solutions. Seriously, they are there for a reason. – Tanel Suurhans Jun 25 '14 at 21:40
  • 2
    @TanelSuurhans As the OP of this answer I agree with all the sentiments regarding "use proven tested tools". Just at the time of this answer, such tools were not core yet, and author appeared to have golfing as an objective. Understanding how your language works is just as important as using tools written in the language. – Kent Fredric Jun 26 '14 at 09:45
  • 6
    The solutions with `shuffle` wouldn't be that awesome, as they'd generate strings with one occurrence of a character ( so string such as "gbalgqut" would be impossible - double "g" ) – PL J Jun 25 '15 at 09:41
  • If golf is the objective, `('a'..'z')` is identical to `(?a..?z)`; two characters saved. – aidan Sep 18 '18 at 06:37
820

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:

  • base64
  • random_bytes
  • random_number

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

rogerdpack
  • 50,731
  • 31
  • 212
  • 332
christopherstyles
  • 1,423
  • 1
  • 10
  • 4
  • This generates strings that aren't safe for passing as get variables for websites - for example, you can get string=+fGH1 (+ is read as a space), and string=fyhi/ (the slash is used for navigation) – stringo0 Aug 14 '10 at 21:58
  • 11
    base64 would, but not hex like in his example – Jeff Dickey Jan 09 '11 at 22:39
  • 68
    By the way, it's part of the stdlib in 1.9 and recent 1.8 versions, so one can just `require 'securerandom'` to get this neat `SecureRandom` helper :) – J-_-L May 17 '11 at 21:14
  • @JeffDickey no, thats wrong. `SecureRandom.base64 => "7p0lfNClJwolM9BJcgG5lQ=="` as you see, there are `=` in it. Method hex only returns `0-9a-f` – Thomas Jan 11 '12 at 11:59
  • 21
    BTW SecureRandom was removed from ActiveSupport in version 3.2. From the changelog: "Removed ActiveSupport::SecureRandom in favor of SecureRandom from the standard library". – Marc Jan 20 '12 at 21:57
  • 16
    `SecureRandom.random_number(36**12).to_s(36).rjust(12, "0")` will generate a string with 0-9a-z (36 characters) that is ALWAYS 12 characters long. Change 12 to whatever length you want. Unfortunately no way to just get A-Z using `Integer#to_s`. – Gerry Shaw Apr 18 '14 at 01:41
  • 1
    @stringo0 that is wrong. If you wanted to pass `+fGH1` through a URL, you just need to URL-encode it like you would ANY value that's going through a URL: `%2BfGH1` – nzifnab Mar 31 '15 at 21:08
  • To generate a string containing only 5 alpha characters you could do this `SecureRandom.hex.gsub(/\d/, "")[0..4]` – user1491929 Feb 01 '16 at 16:14
  • Hi, I like SecureRandom, but how do I get a string of 6 numbers, each digit with range 0-9. And I would like it in a way that it's easy to stub in my Rails controller spec. – gogofan May 17 '17 at 17:11
  • 1
    I tried 'SecureRandom.random_number(9**6)' but it sometimes returns 5 numbers but it sometimes returns 6 numbers. – gogofan May 17 '17 at 17:13
  • What are the chances of getting a duplicate? – dcangulo Nov 07 '18 at 09:20
258

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.

Daniel
  • 2,640
  • 1
  • 20
  • 40
  • 5
    +1 for the shortest version (that doesn't call external binaries ^^). If the random string isn't public facing, I sometimes even just use `rand.to_s`; ugly, but works. – Jo Liss Feb 03 '11 at 12:43
  • 12
    This is a great solution (and fast, too), but it will occasionally produce a string *under* `length` length, roughly once in ~40 – Brian E Sep 10 '11 at 12:13
  • 7
    @Brian E this would guarantee the digits you want: `(36**(length-1) + rand(36**length)).to_s(36)`. 36**(length-1) converted to base 36 is 10**(length-1), which is the smallest value that has the digit length you want. – Eric Hu Oct 07 '11 at 22:01
  • 1
    @EricHu You're solution also produce tokens longer than `length`. – Adrien Jarthon Jan 10 '13 at 21:09
  • 11
    Here is the version always producing tokens of the desired length: `(36**(length-1) + rand(36**length - 36**(length-1))).to_s(36)` – Adrien Jarthon Jan 10 '13 at 21:10
  • Yep, +1 for brevity and lack of dependencies. – Max Williams Mar 13 '14 at 15:45
  • @BigBourin Your solution doesnt always produce tokens of desired length – justcode Jun 20 '14 at 15:58
  • 3
    This spits out an error for me in Rails 4 and Ruby 2.1.1: `NameError: undefined local variable or method `length' for main:Object` – kakubei Nov 14 '14 at 14:52
  • @BrianE I run out of patience before it finishes: `while true do v = rand(36 ** 2); l = v.to_s(36).length; if l > 2 then puts v end end` – x-yuri Jan 19 '15 at 17:22
  • @x-yuri That's because you don't test for strings under desired length... –  Jul 06 '16 at 21:25
  • This will give you an exact length: `Random.new.rand( 36**(length-1) ... 36**length ).to_s( 36 )` –  Jul 06 '16 at 21:42
  • If you need speed and exact length, you can also use this, which runs about 20x faster than the one in my last comment: `( rand * (36**length - 36**(length-1) ) + 36**(length-1) ).floor.to_s( 36 )`. It's not as readable though, so I would put it in a function. Probably the simplest would be to generate something that is guaranteed to be to long, and then truncate it. –  Jul 06 '16 at 22:55
  • I'm using this to generate 6 char tokens in a very simple way: `rand(36**11).to_s(36).upcase[0,6]` i.e. just make the original string safely long enough and then take any N chars from it – Jan Klimo Jan 31 '17 at 09:34
177

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
Michael Durrant
  • 84,444
  • 83
  • 284
  • 429
  • Thanks, this is exactly what I wanted to do. – anisoptera Apr 27 '10 at 06:57
  • Couldn't you just use `charset.sample` instead of `charset.to_a[rand(charset.size)`? – Tom Corelis Nov 06 '11 at 16:54
  • @Tom, if you are using Ruby 1.9 or Rails then yes. Plain 1.8.7 then no. – gtd Feb 29 '12 at 14:54
  • 3
    Is 'U' ambiguous or is that a typo? – gtd Feb 29 '12 at 14:57
  • 11
    @gtd - Yep. U and V are ambigvovs. – colinm Aug 05 '13 at 22:54
  • 1
    @colinm V's in there though. – gtd Aug 05 '13 at 23:11
  • 8
    To be secure you would also want to use `SecureRandom.random_number(charset.size)` instead of `rand(charset.size)` – gtd Oct 10 '13 at 13:11
  • 1
    I was just trying to improve on this, by adding lower case and/or some special characters (shift+num), and got the following lists: `%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 ! @ # $ % ^ & * +}` and `%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 ! @ # $ % ^ & * }` (first list doesn't include lower case, but it's longer) Kinda interesting how that worked out. – dkniffin Oct 15 '14 at 17:28
133

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.

Dennis
  • 46,786
  • 25
  • 128
  • 129
Travis Reeder
  • 31,147
  • 12
  • 77
  • 80
92

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.

the Tin Man
  • 150,910
  • 39
  • 198
  • 279
Markus
  • 5,201
  • 2
  • 43
  • 61
49
[*('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

Shai Coleman
  • 718
  • 11
  • 15
  • 5
    Only problem is each character in the result is unique. Limits the possible values. – tybro0103 May 03 '12 at 15:04
  • 1
    If this [feature request](http://www.ruby-forum.com/topic/822646) goes through, Ruby 1.9.x may end up with #sample for sampling without replacment and #choice for sampling with replacement. – David J. Jun 26 '12 at 16:19
  • This is an error, i think you need ... `[*("A".."Z")]'` ; ((not single qoutes)) – jayunit100 Jan 26 '16 at 15:30
  • can you tell me how i can `stub` this for `rspec` to pass. – Sinscary Jul 25 '16 at 13:12
31

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
ataravati
  • 8,159
  • 5
  • 47
  • 70
Travis Reeder
  • 31,147
  • 12
  • 77
  • 80
28
require 'securerandom'
SecureRandom.urlsafe_base64(9)
LENZCOM
  • 1
  • 2
  • 2
26

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

kaybee99
  • 3,862
  • 2
  • 32
  • 33
  • It doesn't generate a string of length `n`, it actually generates a string that's 4/3 of `n`. – Omar Ali Mar 27 '16 at 18:31
  • @OmarAli you're wrong. As per Ruby documentation in case of `.hex` it's 2n. Documentation: SecureRandom.hex generates a random hexadecimal string. The argument _n_ specifies the length, in bytes, of the random number to be generated. The length of the resulting hexadecimal string is twice of _n_. – Rihards Feb 10 '20 at 11:47
13

Array.new(n){[*"0".."9"].sample}.join, where n=8 in your case.

Generalized: Array.new(n){[*"A".."Z", *"0".."9"].sample}.join, etc.

From: "Generate pseudo random string A-Z, 0-9".

the Tin Man
  • 150,910
  • 39
  • 198
  • 279
gr8scott06
  • 811
  • 11
  • 19
12

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
the Tin Man
  • 150,910
  • 39
  • 198
  • 279
Awais
  • 1,663
  • 19
  • 24
  • 2
    You should not use this to generate yourself a password, as this method will never repeat a character. Therefore you're only using `P(36, 8) / 36^8 = 0.4` of the possible character space for 8 characters (~2x easier to brute force) or `P(36, 25) / 36^25 = 0.00001` of the possible character space for 25 characters (~100,000x easier to brute force). – Ben Carlsson Jul 07 '17 at 19:26
11

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"
Travis
  • 10,236
  • 3
  • 21
  • 38
Ragmaanir
  • 2,523
  • 1
  • 19
  • 17
  • 1
    The `map` solution is really nice! – Misha Moroshko Dec 09 '10 at 09:45
  • You can ask `#sample` for how many elements you want. E.g. `ALPHABET.sample(10).join`... https://ruby-doc.org/core-2.4.0/Array.html#method-i-sample – odlp Nov 21 '17 at 11:26
  • That is actually wrong. `sample(10)` gives you 10 unique samples. But you want to allow them to repeat. But I would use `Array.new(10).map` for performance – Saša Zejnilović Feb 07 '18 at 19:46
  • I wanted alphanumeric with both lower and upper cases. I've also switched to use `Array.new` and its block syntax. `Array.new(20) { [*'0'..'9', *'a'..'z', *'A'..'Z'].sample }.join` – taylorthurlow Apr 12 '18 at 21:35
11
require 'sha1'
srand
seed = "--#{rand(10000)}--#{Time.now}--"
Digest::SHA1.hexdigest(seed)[0,8]
8

Here is one simple code for random password with length 8:

rand_password=('0'..'z').to_a.shuffle.first(8).join
the Tin Man
  • 150,910
  • 39
  • 198
  • 279
Thaha kp
  • 3,573
  • 1
  • 22
  • 25
8

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
pencil
  • 1,130
  • 8
  • 20
  • This is probably the "most" secure solution. SecureRandom attempts to use underlying security APIs provided by the operating system. If you have OpenSSL it will use that, if you're on Windows it will go the best option there. I especially like this solution because it allows you to specify a set of characters for use. Though it won't work if your character set is longer than the maximum value of a byte: 255. I recommend viewing the source code for SecureRandom in the doc: http://ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/rdoc/SecureRandom.html#method-c-random_bytes – Breedly Feb 16 '17 at 16:28
7

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]
the Tin Man
  • 150,910
  • 39
  • 198
  • 279
user163365
  • 1,257
  • 9
  • 14
  • Even better to grab the least significant part of the random number by using the right hand side of the string: rand(2**64).to_s(36)[-10,10] – Jeff Jan 11 '12 at 21:23
6
SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')

Something from Devise

Teej
  • 12,352
  • 9
  • 68
  • 92
  • Why is it replacing string characters using `.tr('+/=lIO0', 'pqrsxyz')`? – miguelcobain Sep 09 '13 at 10:37
  • The special characters because they are not URL safe. And l/I or O/0 because they are very easy confused if you use the technique to generate readable user passwords. – ToniTornado Nov 05 '13 at 13:46
  • That function has a bit of bias towards certain characters. Also for other lengths (e.g. 16), the last character will not be random. Here's a way to avoid that. SecureRandom.base64(64).tr('+/=lIO01', '')[0,16] – Shai Coleman Nov 21 '14 at 13:59
5

Just adding my cents here...

def random_string(length = 8)
  rand(32**length).to_s(32)
end
Anthony
  • 11,352
  • 9
  • 65
  • 100
pdu
  • 9,917
  • 4
  • 53
  • 87
  • 3
    NB: this doesn't always return a string exactly +length+ long - it may be shorter. It depends on the the number returned by `rand` – tardate Sep 20 '12 at 11:23
5

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
the Tin Man
  • 150,910
  • 39
  • 198
  • 279
Tilo
  • 31,645
  • 4
  • 72
  • 101
5

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

Easily modified

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
Community
  • 1
  • 1
Nathan Long
  • 113,812
  • 91
  • 316
  • 418
  • 1
    i would recommend this to just use capital letters + numbers, also remove the "confusing" ones 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 – luster Jun 24 '14 at 19:36
4

My favorite is (:A..:Z).to_a.shuffle[0,8].join. Note that shuffle requires Ruby > 1.9.

Josh
  • 5,305
  • 1
  • 26
  • 52
4

This solution needs external dependency, but seems prettier than another.

  1. Install gem faker
  2. Faker::Lorem.characters(10) # => "ang9cbhoa8"
asiniy
  • 11,816
  • 6
  • 49
  • 117
  • Passing number with the 1st argument of characters is deprecated. Use keyword argument like `characters(number: ...)` instead. `Faker::Lorem.characters(number: 10) # => "ang9cbhoa8"` – Mr. Rene Feb 12 '21 at 03:02
4

Given:

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

Single expression, can be passed as an argument, allows duplicate characters:

Array.new(len) { chars.sample }.join
henrebotha
  • 1,098
  • 2
  • 16
  • 33
Tim James
  • 1,353
  • 1
  • 13
  • 15
3

My 2 cents:

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

I just write a small gem random_token to generate random tokens for most use case, enjoy ~

https://github.com/sibevin/random_token

Sibevin Wang
  • 4,122
  • 3
  • 25
  • 26
3

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.

the Tin Man
  • 150,910
  • 39
  • 198
  • 279
erik
  • 1
  • 1
3
''.tap {|v| 4.times { v << ('a'..'z').to_a.sample} }
animuson
  • 50,765
  • 27
  • 132
  • 142
eric
  • 1
  • 1
3

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
webmat
  • 50,648
  • 12
  • 52
  • 59
  • Why not use `(CHARS*length).sample(length).join`? – niels Jun 20 '13 at 19:31
  • @niels This suggestion would generate a *weighted* string, in favour of non-repeated characters. For example, if `CHARS=['a','b']` then your method would generate `"aa"` or `"bb"` only 33% of the time, but `"ab"` or `"ba"` 67% of the time. Maybe that's not a problem, but it's worth bearing in mind! – Tom Lord Dec 14 '17 at 23:27
  • good point, @TomLord, I think I didn't actually realise that when I posted that suggestion (although I must admit I don't remember posting that at all :D) – niels Dec 19 '17 at 21:01
3

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

One Character from each Range.

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"
Joshua Pinter
  • 37,288
  • 19
  • 208
  • 218
peter
  • 40,314
  • 5
  • 58
  • 99
  • And if you want to enforce a certain number of each range, you could do something like this: `( ('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 Aug 22 '19 at 03:47
2

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.

lzap
  • 14,745
  • 10
  • 62
  • 100
2

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

lzap
  • 14,745
  • 10
  • 62
  • 100
2

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.

Andro Selva
  • 51,960
  • 51
  • 189
  • 237
2

Here is another method:

  • It uses the secure random number generator instead of rand()
  • Can be used in URLs and file names
  • Contains uppercase, lowercase characters and numbers
  • Has an option not to include ambiguous characters I0l01

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
Evgenii
  • 33,381
  • 26
  • 125
  • 160
2

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.

Carlos Villela
  • 515
  • 5
  • 11
  • 1
    Your approach can't return repeating characters (e.g, uuunMoBldj)... Is this what's wanted? – webmat Sep 18 '08 at 02:32
  • 2
    Yes, I suppose that technically isn't really a random string anymore, good find webmat. – Jeff Sep 21 '08 at 01:50
2

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
Ryan Bigg
  • 102,687
  • 22
  • 224
  • 252
1

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.

Chris Bloom
  • 3,383
  • 1
  • 29
  • 45
1
10.times do 
  alphabet = ('a'..'z').to_a
  string += alpha[rand(alpha.length)]
end
ndnenkov
  • 33,260
  • 9
  • 67
  • 97
DDD
  • 393
  • 1
  • 4
  • 12
1

For devise secure_validatable you can use this

(0...8).map { ([65, 97].sample + rand(26)).chr }.push(rand(99)).join

shiva kumar
  • 10,194
  • 4
  • 20
  • 24
  • To generate a random password using uppercase, lowercase, numbers, and special characters, I used something like: `random_ascii_character = -> { (33..126).to_a.sample.chr }; 8.times.map { random_ascii_character.call }.shuffle.join` – Kyle Tolle Jul 25 '16 at 22:13
1

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

Lucas Andrade
  • 2,353
  • 2
  • 19
  • 38
0

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"
Ghazi
  • 640
  • 7
  • 14
0

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.

Abdo
  • 12,299
  • 7
  • 72
  • 94
0
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
Automatico
  • 10,712
  • 7
  • 67
  • 106
0
`pwgen 8 1`.chomp
Nathan L Smith
  • 143
  • 1
  • 4
0
a='';8.times{a<<[*'a'..'z'].sample};p a

or

8.times.collect{[*'a'..'z'].sample}.join
Minski
  • 21
  • 2
0

Use 'SafeRandom' Gem GithubLink

It will provide the easiest way to generate random values for Rails2, Rails 3, Rails 4, Rails 5 compatible.

Rubyist
  • 6,247
  • 8
  • 46
  • 83
0

To make your first into one statement:

(0...8).collect { |n| value  << (65 + rand(25)).chr }.join()
Kevin Conner
  • 8,437
  • 4
  • 41
  • 50
-1

In ruby 1.9 one can use Array's choice method which returns random element from array

maykeye
  • 1,266
  • 1
  • 12
  • 16
  • 1
    Thanks for the heads up - however, http://svn.ruby-lang.org/repos/ruby/tags/v1_9_1_0/NEWS seems to indicate that Array#sample is to be used in 1.9.1, not Array#choose / choice – Andrew Grimm Aug 31 '09 at 05:37
-1

This is almost as ugly but perhaps as step in right direction?

 (1..8).map{|i| ('a'..'z').to_a[rand(26)]}.join
Purfideas
  • 3,150
  • 22
  • 17
-11

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.

nsayer
  • 15,789
  • 2
  • 29
  • 50
  • 5
    If you don't know Ruby, why bother to answer? :/ – Ardee Aram May 08 '13 at 09:59
  • 1
    Because generating user enterable random strings that look like 0OiIlZ25S makes baby Jesus cry, and I wanted to point out that if the random string was something humans would need to ponder, then JUST PERHAPS it might behoove someone to consider that, Mr. Hall Monitor. – nsayer May 08 '13 at 16:20