В настоящее время я создаю псевдослучайную строку из 8 символов в верхнем регистре для "A" .. "Z":
value = ""; 8.times{value << (65 + rand(25)).chr}
но он не выглядит чистым и не может быть передан в качестве аргумента, поскольку это не единичный оператор. Чтобы получить строку со смешанным регистром "a" .. "z" плюс "A" .. "Z", я изменил ее на:
value = ""; 8.times{value << ((rand(2)==1?65:97) + rand(25)).chr}
но похоже на хлам.
Есть ли у кого-нибудь способ получше?
Предположим, существует метод сброса пароля пользователя, и у него есть аргумент для нового пароля. Я хотел бы передать случайную строку, в приведенном выше коде мне нужна переменная tmp, тогда как в приведенных ниже примерах с одним оператором я могу сделать все это как один лайнер. Конечно, вспомогательный метод может быть приятным в долгосрочной перспективе, особенно если мне нужно что-то похожее здесь и там, но иногда вы просто хотите, чтобы он был на месте, один раз, готов.
Нет, вам не нужно использовать временную переменную. Попробуйте это: reset_user_password!(random_string), где def random_string; SecureRandom.urlsafe_base64(20) end
8 букв - позорно слабый пароль. Учитывая md5sum, современный компьютер может восстановить пароль в 30 секунд. Как насчет чего-нибудь длиннее securerandom.urlsafe_base64
Лол, хорошо, что спросили в 2008 году, конечно, давайте поднимем это до 15 или около того.
Почему на это так много ответов? Не то чтобы это бесполезный вопрос, но мне любопытно, как он привлек внимание.
хорошо, поставьте веб-дизайнера перед VIM и попросите его сохранить и выйти; Шутки отдельно. require 'securerandom'; SecureRandom.hex(15) должен работать нормально

Чтобы превратить ваше первое в одно заявление:
(0...8).collect { |n| value << (65 + rand(25)).chr }.join()
(0...8).map { (65 + rand(26)).chr }.join
Я слишком много времени провожу за игрой в гольф.
(0...50).map { ('a'..'z').to_a[rand(26)] }.join
И последнее, еще более запутанное, но более гибкое и требующее меньше циклов:
o = [('a'..'z'), ('A'..'Z')].map(&:to_a).flatten
string = (0...50).map { o[rand(o.length)] }.join
34 символа и молниеносно: ('a'..'z').to_a.shuffle[0,8].join. Обратите внимание, что вам понадобится Ruby> = 1.9 для shuffle.
первый пример должен быть rand (26), иначе вы никогда не получите букву 'Z'
Использование существующих библиотек предпочтительнее, если у вас нет собственного драйвера. См. SecureRandom в качестве одного примера, в других ответах.
@faraz ваш метод функционально не тот, он не случайный с заменой.
Туш @michaeltwofish. Вот самый короткий и самый производительный однострочный текст, который я смог собрать: [].fill(0,l){rand(65..90).chr}.join (35 символов). fill быстрее, чем map, но для реальной производительности отбросьте гольф и предварительно создайте массив с использованием правильный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. Это даже не намного быстрее для 8 символов. С 8 символами не беспокойтесь. Что-нибудь летает. ;) Подробности смотрите в моем полные тесты.
[*('a'..'z'),*('0'..'9')].shuffle[0,8].join для генерации случайной строки из букв и цифр.
rand детерминирован и предсказуем. Не используйте это для генерации паролей! Вместо этого используйте одно из решений SecureRandom.
последний пример должен читать (0...50).map { [('a'..'z'),('A'..'Z')].flat_map(&:to_a).sample }.join, вы определенно нет потратили тоже много времени на игру в гольф xD
далее сокращено из ответа Денниса (0..50).map { [*'a'..'z',*'A'..'Z'].sample }.join
Я только что создал модуль RandomString, обобщающий три основных метода, упомянутых во всех ответах: gist.github.com/jopotts/8706578
Всегда используйте существующие решения, такие как SecureRandom, в пользу собственных «случайных» решений. Серьезно, они здесь не зря.
@TanelSuurhans Как ОП этого ответа я согласен со всеми мнениями относительно «использовать проверенные проверенные инструменты». Как раз на момент этого ответа такие инструменты еще не были основными, и похоже, что целью автора была игра в гольф. Понимание того, как работает ваш язык, так же важно, как и использование инструментов, написанных на этом языке.
Решения с shuffle не будут такими уж хорошими, поскольку они будут генерировать строки с одним вхождением символа (поэтому такая строка, как "gbalgqut", будет невозможна - двойная "g")
Если целью является гольф, ('a'..'z') идентичен (?a..?z); два персонажа сохранены.
Я не знаю Ruby, поэтому я не могу дать вам точный синтаксис, но я бы установил постоянную строку со списком допустимых символов, а затем использовал бы оператор подстроки, чтобы выбрать из нее случайный символ.
Преимущество здесь в том, что если предполагается, что строка может вводиться пользователем, вы можете исключить легко запутанные символы, такие как l и 1 и i, 0 и O, 5 и S и т. д.
Если вы не знаете Руби, зачем вообще отвечать? : /
Поскольку генерация вводимых пользователем случайных строк, которые выглядят как 0OiIlZ25S, заставляет младенца Иисуса плакать, и я хотел указать, что если случайная строка была чем-то, над чем люди должны были бы подумать, то ТОЛЬКО ВОЗМОЖНО, это могло бы быть целесообразно, мистер Холл Монитор.
Это почти так же уродливо, но, может быть, как шаг в правильном направлении?
(1..8).map{|i| ('a'..'z').to_a[rand(26)]}.join
:) Я вообще-то извращенно люблю самый быстрый пистолет
require 'sha1'
srand
seed = "--#{rand(10000)}--#{Time.now}--"
Digest::SHA1.hexdigest(seed)[0,8]
Интересно, но немного дороже в вычислительном отношении
Также нет возможности для ограниченного набора персонажей.
Имейте в виду, что шестнадцатеричный дайджест возвращает только символы 0–9 и a-f.
Мы использовали это в нашем коде:
class String
def self.random(length=10)
('a'..'z').sort_by {rand}[0,length].join
end
end
Максимальная поддерживаемая длина - 25 (в любом случае мы используем ее только со значением по умолчанию, поэтому проблем не возникло).
Кто-то упомянул, что "a" .. "z" неоптимально, если вы хотите полностью избежать появления оскорбительных слов. Одна из наших идей заключалась в том, чтобы удалить гласные, но вы все равно получите WTFBBQ и т. д.
Ваш подход не может возвращать повторяющиеся символы (например, uuunMoBldj) ... Это то, что нужно?
Да, я полагаю, что технически это больше не случайная строка, хороший веб-сайт для поиска.
С помощью этого метода вы можете передавать произвольную длину. По умолчанию установлено 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
Я думаю, что пока что мне больше всего нравится ответ Радара. Я бы немного подправил вот так:
CHARS = ('a'..'z').to_a + ('A'..'Z').to_a
def rand_string(length=8)
s=''
length.times{ s << CHARS[rand(CHARS.length)] }
s
end
Почему бы не использовать (CHARS*length).sample(length).join?
@niels Это предложение сгенерирует строку взвешенный в пользу неповторяющихся символов. Например, если CHARS=['a','b'], то ваш метод будет генерировать "aa" или "bb" только в 33% случаев, а "ab" или "ba" - в 67% случаев. Может быть, это не проблема, но об этом стоит помнить!
хороший момент, @TomLord, я думаю, что на самом деле я этого не осознавал, когда размещал это предложение (хотя, должен признать, я вообще не помню, чтобы публиковал это: D)
Это решение генерирует строку легко читаемых символов для кодов активации; Я не хотел, чтобы люди путали 8 с B, 1 с I, 0 с O, L с 1 и т. д.
# 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
Спасибо, это именно то, что я хотел сделать.
Не могли бы вы просто использовать charset.sample вместо charset.to_a[rand(charset.size)?
@Tom, если вы используете Ruby 1.9 или Rails, тогда да. Обычная 1.8.7 то нет.
"U" двусмысленно или это опечатка?
@gtd - Ага. U и V - амбигвовы.
Хотя @colinm V там.
В целях безопасности вы также можете использовать SecureRandom.random_number(charset.size) вместо rand(charset.size).
Я просто пытался улучшить это, добавляя строчные буквы и / или некоторые специальные символы (shift + num), и получил следующие списки: %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 ! @ # $ % ^ & * +} и %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 ! @ # $ % ^ & * } (первый список не включает нижний регистр, но он длиннее) Довольно интересно как это сработало.
Я не могу вспомнить, где я это нашел, но мне кажется, что это лучший и наименее интенсивный процесс:
def random_string(length=10)
chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789'
password = ''
length.times { password << chars[rand(chars.size)] }
password
end
Возможно, вы нашли это здесь? travisonrails.com/2007/06/07/generate-random-text-with-ruby
Разве здесь не пропущены 0 и 1?
Похоже, это могло быть именно там, где я это нашел.
И да, похоже, что 0 и 1 отсутствуют.
0 и 1, а также O и I были намеренно пропущены, потому что эти символы неоднозначны. Если этот вид кода используется для генерации набора символов, которые пользователь должен скопировать, лучше всего исключить символы, которые может быть трудно различить вне контекста.
В Ruby 1.9 можно использовать метод выбора массива, который возвращает случайный элемент из массива.
Спасибо за внимание - однако svn.ruby-lang.org/repos/ruby/tags/v1_9_1_0/NEWS, похоже, указывает на то, что образец Array # должен использоваться в 1.9.1, а не Array # choose / choice
Почему бы не использовать SecureRandom?
require 'securerandom'
random_string = SecureRandom.hex
# outputs: 5b5cd0da3121fc53b4bc84d0c8af2e81 (i.e. 32 chars of 0..9, a..f)
SecureRandom также имеет методы для:
см .: http://ruby-doc.org/stdlib-1.9.2/libdoc/securerandom/rdoc/SecureRandom.html
Это генерирует строки, которые небезопасно передавать в качестве переменных get для веб-сайтов - например, вы можете получить string = + fGH1 (+ читается как пробел) и string = fyhi / (косая черта используется для навигации)
base64 будет, но не шестнадцатеричным, как в его примере
Между прочим, это часть stdlib в версиях 1.9 и последних 1.8, так что можно просто require 'securerandom' получить этот аккуратный помощник SecureRandom :)
@JeffDickey нет, это неправильно. SecureRandom.base64 => "7p0lfNClJwolM9BJcgG5lQ= = " как видите, в нем есть =. Шестнадцатеричный метод возвращает только 0-9a-f
Кстати, SecureRandom был удален из ActiveSupport в версии 3.2. Из журнала изменений: «Удален ActiveSupport :: SecureRandom в пользу SecureRandom из стандартной библиотеки».
SecureRandom.random_number(36**12).to_s(36).rjust(12, "0") сгенерирует строку с 0-9a-z (36 символов), которая ВСЕГДА состоит из 12 символов. Измените 12 на любую желаемую длину. К сожалению, нет возможности просто получить A-Z с помощью Integer#to_s.
@ stringo0 это неверно. Если вы хотите передать +fGH1 через URL-адрес, вам просто нужно URL-кодировать его, как если бы вы использовали ЛЮБОЕ значение, которое проходит через URL-адрес: %2BfGH1
Чтобы сгенерировать строку, содержащую только 5 буквенных символов, вы можете сделать это SecureRandom.hex.gsub(/\d/, "")[0..4]
Привет, мне нравится SecureRandom, но как мне получить строку из 6 чисел, каждая цифра находится в диапазоне 0–9. И я бы хотел, чтобы это было легко вставить в мою спецификацию контроллера Rails.
Я пробовал SecureRandom.random_number (9 ** 6), но иногда он возвращает 5 чисел, но иногда возвращает 6 чисел.
Каковы шансы получить дубликат?
Еще один метод, который мне нравится использовать:
rand(2**256).to_s(36)[0..7]
Добавьте ljust, если вы действительно не уверены в правильной длине строки:
rand(2**256).to_s(36).ljust(8,'a')[0..7]
Еще лучше получить наименее значимую часть случайного числа, используя правую часть строки: rand (2 ** 64) .to_s (36) [- 10,10]
`pwgen 8 1`.chomp
Errno :: ENOENT: нет такого файла или каталога - pwgen 8 1
Рубин 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"
Решение map действительно хорошее!
Вы можете спросить у #sample, сколько элементов вам нужно. Например. ALPHABET.sample(10).join ... ruby-doc.org/core-2.4.0/Array.html#method-i-sample
На самом деле это неправильно. sample(10) дает вам 10 уникальных образцов. Но вы хотите позволить им повторяться. Но я бы использовал Array.new(10).map для производительности
Я хотел буквенно-цифровые символы в верхнем и нижнем регистрах. Я также перешел на использование Array.new и его блочного синтаксиса. Array.new(20) { [*'0'..'9', *'a'..'z', *'A'..'Z'].sample }.join
Недавно я делал что-то подобное, чтобы сгенерировать 8-байтовую случайную строку из 62 символов. Символы были 0-9, a-z, A-Z. У меня был их массив, так как он 8 раз зацикливался и выбирал случайное значение из массива. Это было внутри приложения Rails.
str = ''
8.times {|i| str << ARRAY_OF_POSSIBLE_VALUES[rand(SIZE_OF_ARRAY_OF_POSSIBLE_VALUES)] }
Странно то, что у меня много дубликатов. Случайным образом этого никогда не должно происходить. 62 ^ 8 огромен, но из примерно 1200 кодов в db у меня было хорошее количество дубликатов. Я заметил, что они происходят на часовых границах друг друга. Другими словами, я могу увидеть дуплекс в 12:12:23 и 2:12:22 или что-то в этом роде ... не уверен, проблема во времени или нет.
Этот код был перед созданием объекта ActiveRecord. Перед созданием записи этот код запускался и генерировал «уникальный» код. Записи в БД всегда производились надежно, но код (str в строке выше) слишком часто дублировался.
Я создал сценарий для выполнения 100000 итераций этой строки выше с небольшой задержкой, так что это заняло бы 3-4 часа, надеясь увидеть какой-то повторяющийся шаблон на ежечасной основе, но ничего не увидел. Я понятия не имею, почему это происходило в моем приложении Rails.
Я использую это для генерации случайных дружественных строк URL с гарантированной максимальной длиной:
string_length = 8
rand(36**string_length).to_s(36)
Он генерирует случайные строки строчных букв a-z и 0-9. Он не очень настраиваемый, но короткий и чистый.
+1 для самой короткой версии (которая не вызывает внешние двоичные файлы ^^). Если случайная строка не является общедоступной, я иногда даже просто использую rand.to_s; некрасиво, но работает.
Это отличное решение (и быстрое тоже), но время от времени оно будет выдавать строку подlength length, примерно один раз из ~ 40
@Brian E, это гарантировало бы нужные цифры: (36**(length-1) + rand(36**length)).to_s(36). 36 ** (длина-1), преобразованная в основание 36, составляет 10 ** (длина-1), что является наименьшим значением, имеющим желаемую длину цифры.
@EricHu Ваше решение также производит токены длиннее, чем length.
Вот версия, всегда производящая токены желаемой длины: (36**(length-1) + rand(36**length - 36**(length-1))).to_s(36)
Ага, +1 за краткость и отсутствие зависимостей.
@BigBourin Ваше решение не всегда производит токены желаемой длины
Это выдает мне ошибку в Rails 4 и Ruby 2.1.1: NameError: undefined local variable or method length 'для main: Object`
@BrianE У меня заканчивается терпение, прежде чем он закончится: while true do v = rand(36 ** 2); l = v.to_s(36).length; if l > 2 then puts v end end
@ x-yuri Это потому, что вы не проверяете строки нужной длины ...
Это даст вам точную длину: Random.new.rand( 36**(length-1) ... 36**length ).to_s( 36 )
Если вам нужна скорость и точная длина, вы также можете использовать это, которое работает примерно в 20 раз быстрее, чем тот, который был в моем последнем комментарии: ( rand * (36**length - 36**(length-1) ) + 36**(length-1) ).floor.to_s( 36 ). Однако он не такой читабельный, поэтому я бы поместил его в функцию. Вероятно, самым простым было бы сгенерировать что-то, что гарантированно будет слишком длинным, а затем обрезать его.
Я использую это для генерации 6 токенов символов очень простым способом: rand(36**11).to_s(36).upcase[0,6], т.е. просто сделайте исходную строку достаточно длинной, а затем возьмите из нее любые N символов
попробуйте это
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
Мне нравятся ответы в этой ветке, они действительно были очень полезны! Но если я могу сказать, ни один из них не удовлетворяет меня, возможно, это метод rand (). мне это просто не кажется правильным, поскольку у нас есть метод выбора Array # на этот счет.
Данный:
chars = [*('a'..'z'),*('0'..'9')].flatten
Одно выражение, которое может быть передано в качестве аргумента, допускает повторяющиеся символы:
Array.new(len) { chars.sample }.join
Я думаю, что это хороший баланс лаконичности, ясности и простоты модификации.
characters = ('a'..'z').to_a + ('A'..'Z').to_a
# Prior to 1.9, use .choice, not .sample
(0..8).map{characters.sample}.join
Например, включая цифры:
characters = ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a
Прописные шестнадцатеричные:
characters = ('A'..'F').to_a + (0..9).to_a
Для действительно впечатляющего набора персонажей:
characters = (32..126).to_a.pack('U*').chars.to_a
я бы порекомендовал использовать только заглавные буквы + цифры, а также удалить "сбивающие с толку" 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
Вот еще один способ:
Требуется 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
Другие упоминали нечто подобное, но при этом используется функция защиты URL-адресов.
require 'securerandom'
p SecureRandom.urlsafe_base64(5) #=> "UtM7aa8"
p SecureRandom.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
p SecureRandom.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ= = "
Результат может содержать A-Z, a-z, 0-9, «-» и «_». «=» Также используется, если заполнение истинно.
Это именно то, что я искал. Спасибо!
''.tap {|v| 4.times { v << ('a'..'z').to_a.sample} }
[*('A'..'Z')].sample(8).join
Создать случайную строку из 8 букв (например, NVAYXHGR)
([*('A'..'Z'),*('0'..'9')]-%w(0 1 I O)).sample(8).join
Сгенерировать случайную строку из 8 символов (например, 3PH4SWF2), исключая 0/1 / I / O. Рубин 1.9
Проблема только в том, что каждый символ в результате уникален. Ограничивает возможные значения.
Если этот запрос функции проходит, Ruby 1.9.x может закончить с #sample для выборки без замены и #choice для выборки с заменой.
Это ошибка, думаю нужно ... [*("A".."Z")]'; ((не одинарные кавычки))
Можете ли вы сказать мне, как я могу пройти stub, чтобы пройти rspec?
2 решения для случайной строки, состоящей из 3 диапазонов:
(('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)*""
И если вам нужен хотя бы один символ из каждого диапазона, например, создать случайный пароль, состоящий из одной прописной буквы, одной строчной буквы и одной цифры, вы можете сделать что-то вроде этого:
( ('a'..'z').to_a.sample(8) + ('A'..'Z').to_a.sample(8) + (0..9).to_a.sample(8) ).shuffle.join
#=> "Kc5zOGtM0H796QgPp8u2Sxo1"
И если вы хотите обеспечить соблюдение определенного количества каждого диапазона, вы можете сделать что-то вроде этого: ( ('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"
SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
Что-то от Devise
Почему он заменяет строковые символы с помощью .tr('+/=lIO0', 'pqrsxyz')?
Специальные символы, потому что они не безопасны для URL. И l / I или O / 0, потому что их очень легко перепутать, если вы используете технику для генерации читаемых паролей пользователей.
Эта функция имеет некоторую предвзятость по отношению к определенным персонажам. Также для других длин (например, 16) последний символ не будет случайным. Вот способ этого избежать. SecureRandom.base64 (64) .tr ('+ / = lIO01', '') [0,16]
Это основано на нескольких других ответах, но добавляет немного больше сложности:
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
По сути, он обеспечивает пароль длиной от 8 до 20 символов, который содержит как минимум одну цифру и один специальный символ.
Мои 2 цента:
def token(length=16)
chars = [*('A'..'Z'), *('a'..'z'), *(0..9)]
(0..length).map {chars.sample}.join
end
Просто добавляю сюда свои центы ...
def random_string(length = 8)
rand(32**length).to_s(32)
end
NB: это не всегда возвращает строку точная + длинная - она может быть короче. Это зависит от числа, возвращаемого rand.
Если вы используете UNIX и по-прежнему должны использовать Ruby 1.8 (без SecureRandom) без Rails, вы также можете использовать это:
random_string = `openssl rand -base64 24`
Обратите внимание, что это порождает новую оболочку, это очень медленно и может быть рекомендовано только для скриптов.
require 'securerandom'
SecureRandom.urlsafe_base64(9)
SecureRandom - отличная библиотека. Я не знал, что это было там. Спасибо.
старая школа (и уродливая) альтернатива: Random.new.bytes(9)
Кстати, urlsafe_base64 возвращает строку примерно 4/3 указанной длины. Чтобы получить строку длиной ровно n символов, попробуйте n=9 ; SecureRandom.urlsafe_base64(n)[0..n-1]
Вот один простой код для случайного пароля длиной 8:
rand_password=('0'..'z').to_a.shuffle.first(8).join
Имейте в виду: rand предсказуем для злоумышленника и, следовательно, вероятно, небезопасен. Вам обязательно следует использовать SecureRandom, если он предназначен для генерации паролей. Я использую что-то вроде этого:
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
Это, наверное, «самое» безопасное решение. SecureRandom пытается использовать базовые API безопасности, предоставляемые операционной системой. Если у вас есть OpenSSL, он будет использовать его, если вы работаете в Windows, он будет лучшим вариантом. Мне особенно нравится это решение, потому что оно позволяет вам указать набор символов для использования. Хотя это не сработает, если ваш набор символов длиннее, чем максимальное значение байта: 255. Я рекомендую просмотреть исходный код SecureRandom в документе: ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/rdoc/…
При необходимости создайте пустую строку или префикс:
myStr = "OID-"
Используйте этот код для заполнения строки случайными числами:
begin; n = ((rand * 43) + 47).ceil; myStr << n.chr if !(58..64).include?(n); end while(myStr.length < 12)
Примечания:
(rand * 43) + 47).ceil
Он будет генерировать случайные числа от 48-91 (0,1,2..Y, Z)
!(58..64).include?(n)
Он используется для пропуска специальных символов (поскольку мне не интересно их включать)
while(myStr.length < 12)
Он сгенерирует строку длиной 12 символов, включая префикс.
Пример вывода:
"OID-XZ2J32XM"
Если вам нужна строка указанной длины, используйте:
require 'securerandom'
randomstring = SecureRandom.hex(n)
Он сгенерирует случайную строку длины 2n, содержащую 0-9 и a-f.
Он не генерирует строку длины n, он фактически генерирует строку, которая составляет 4/3 длины n.
@OmarAli, ты ошибаешься. Согласно документации Ruby в случае .hex это 2n. Документация: SecureRandom.hex генерирует случайную шестнадцатеричную строку. Аргумент п указывает длину в байтах генерируемого случайного числа. Длина результирующей шестнадцатеричной строки вдвое больше п.
Вот решение, которое является гибким и допускает дублирование:
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
Пример:
"ATCG".random(8) => "CGTGAAGA"
Вы также можете разрешить более частое появление определенного персонажа:
"AAAAATCG".random(10) => "CTGAAAAAGC"
Объяснение: Вышеупомянутый метод принимает символы данной строки и генерирует достаточно большой массив. Затем он перемешивает его, берет первые n элементов и присоединяется к ним.
Мой фаворит - (:A..:Z).to_a.shuffle[0,8].join. Обратите внимание, что для перемешивания требуется Ruby> 1.9.
Я просто пишу небольшой драгоценный камень random_token для генерации случайных токенов для большинства случаев использования, наслаждайтесь ~
https://github.com/sibevin/random_token
Отличная жемчужина. Спасибо :)
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
Еще один трюк, который работает с Ruby 1.8+ и работает так же быстро, как:
>> require "openssl"
>> OpenSSL::Random.random_bytes(20).unpack('H*').join
=> "2f3ff53dd712ba2303a573d9f9a8c1dbc1942d28"
Он дает вам случайную шестнадцатеричную строку. Аналогичным образом вы сможете сгенерировать строку base64 ('M *').
Вы можете использовать String#random из Facets of Ruby Gem facets.
В основном он делает это:
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
Array.new(n){[*"0".."9"].sample}.join,
где n=8 в вашем случае.
Обобщенный: Array.new(n){[*"A".."Z", *"0".."9"].sample}.join и др.
Вот однострочный простой код для случайной строки длиной 8:
random_string = ('0'..'z').to_a.shuffle.first(8).join
Вы также можете использовать его для случайного пароля длиной 8:
random_password = ('0'..'z').to_a.shuffle.first(8).join
Вы не должны использовать это для создания пароля, так как этот метод никогда не повторит символ. Следовательно, вы используете только P(36, 8) / 36^8 = 0.4 из возможного пространства символов для 8 символов (~ в 2 раза легче перебором) или P(36, 25) / 36^25 = 0.00001 из возможного пространства символов для 25 символов (~ в 100000 раз проще подобрать грубую силу).
Это решение требует внешней зависимости, но кажется красивее другого.
Faker::Lorem.characters(10) # => "ang9cbhoa8"Передача числа с 1-м аргументом символов устарела. Вместо этого используйте аргумент ключевого слова, например characters(number: ...). Faker::Lorem.characters(number: 10) # => "ang9cbhoa8"
10.times do
alphabet = ('a'..'z').to_a
string += alpha[rand(alpha.length)]
end
Для разработки secure_validatable вы можете использовать это
(0 ... 8) .map {([65, 97] .sample + rand (26)). Chr} .push (rand (99)). Join
Чтобы сгенерировать случайный пароль с использованием прописных и строчных букв, цифр и специальных символов, я использовал что-то вроде: random_ascii_character = -> { (33..126).to_a.sample.chr }; 8.times.map { random_ascii_character.call }.shuffle.join
a='';8.times{a<<[*'a'..'z'].sample};p a
или же
8.times.collect{[*'a'..'z'].sample}.join
Начиная с Ruby 2.5, с SecureRandom.alphanumeric это действительно просто:
len = 8
SecureRandom.alphanumeric(len)
=> "larHSsgL"
Он генерирует случайные строки, содержащие A-Z, a-z и 0-9, и поэтому должен применяться в большинстве случаев использования. И они генерируются случайным образом безопасным образом, что тоже может быть преимуществом.
Это тест для сравнения с решением, получившим наибольшее количество голосов:
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)
Таким образом, решение rand занимает около 3/4 времени, чем SecureRandom. Это может иметь значение, если вы генерируете много строк, но если вы просто время от времени создаете какую-то случайную строку, я всегда буду использовать более безопасную реализацию, поскольку ее также проще вызывать и более явным.
Вот улучшенный ответ @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
В ответах @Travis R символы и числа были вместе, поэтому иногда random_string мог возвращать только числа или только символы. Благодаря этому усовершенствованию, по крайней мере, половина random_string будет состоять из символов, а остальные - из чисел. На всякий случай, если нужна случайная строка с цифрами и символами
Используйте самоцвет SafeRandom GithubLink
Это обеспечит самый простой способ генерации случайных значений для совместимых с Rails2, Rails 3, Rails 4, Rails 5.
Я не понимаю, почему вас волнует, что «поскольку это не единичное утверждение, его нельзя передать в качестве аргумента». Почему бы просто не сделать его служебным или вспомогательным методом?