Как сгенерировать случайную строку в Ruby

В настоящее время я создаю псевдослучайную строку из 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}

но похоже на хлам.

Есть ли у кого-нибудь способ получше?

Я не понимаю, почему вас волнует, что «поскольку это не единичное утверждение, его нельзя передать в качестве аргумента». Почему бы просто не сделать его служебным или вспомогательным методом?

David J. 14.06.2012 22:53

Предположим, существует метод сброса пароля пользователя, и у него есть аргумент для нового пароля. Я хотел бы передать случайную строку, в приведенном выше коде мне нужна переменная tmp, тогда как в приведенных ниже примерах с одним оператором я могу сделать все это как один лайнер. Конечно, вспомогательный метод может быть приятным в долгосрочной перспективе, особенно если мне нужно что-то похожее здесь и там, но иногда вы просто хотите, чтобы он был на месте, один раз, готов.

Jeff 26.06.2012 20:07

Нет, вам не нужно использовать временную переменную. Попробуйте это: reset_user_password!(random_string), где def random_string; SecureRandom.urlsafe_base64(20) end

David J. 26.06.2012 20:13

8 букв - позорно слабый пароль. Учитывая md5sum, современный компьютер может восстановить пароль в 30 секунд. Как насчет чего-нибудь длиннее securerandom.urlsafe_base64

Colonel Panic 13.10.2012 19:46

Лол, хорошо, что спросили в 2008 году, конечно, давайте поднимем это до 15 или около того.

Jeff 27.10.2012 02:17

Почему на это так много ответов? Не то чтобы это бесполезный вопрос, но мне любопытно, как он привлек внимание.

Lou 16.05.2014 23:46

хорошо, поставьте веб-дизайнера перед VIM и попросите его сохранить и выйти; Шутки отдельно. require 'securerandom'; SecureRandom.hex(15) должен работать нормально

illusionist 22.04.2016 10:45
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
782
7
442 001
50
Перейти к ответу Данный вопрос помечен как решенный

Ответы 50

Чтобы превратить ваше первое в одно заявление:

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

fny 14.06.2012 21:35

первый пример должен быть rand (26), иначе вы никогда не получите букву 'Z'

Alex Gaudio 19.06.2012 02:17

Использование существующих библиотек предпочтительнее, если у вас нет собственного драйвера. См. SecureRandom в качестве одного примера, в других ответах.

David J. 26.06.2012 20:15

@faraz ваш метод функционально не тот, он не случайный с заменой.

michaeltwofish 06.07.2012 11:13

Туш @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 символами не беспокойтесь. Что-нибудь летает. ;) Подробности смотрите в моем полные тесты.

fny 07.07.2012 12:33

[*('a'..'z'),*('0'..'9')].shuffle[0,8].join для генерации случайной строки из букв и цифр.

Robin 05.03.2013 17:50

rand детерминирован и предсказуем. Не используйте это для генерации паролей! Вместо этого используйте одно из решений SecureRandom.

pencil 30.03.2013 18:33

последний пример должен читать (0...50).map { [('a'..'z'),('A'..'Z')].flat_map(&:to_a).sample }.join, вы определенно нет потратили тоже много времени на игру в гольф xD

Dennis Krupenik 16.08.2013 17:44

далее сокращено из ответа Денниса (0..50).map { [*'a'..'z',*'A'..'Z'].sample }.join

Vizjerai 10.10.2013 01:03

Я только что создал модуль RandomString, обобщающий три основных метода, упомянутых во всех ответах: gist.github.com/jopotts/8706578

Jo P 30.01.2014 15:21

Всегда используйте существующие решения, такие как SecureRandom, в пользу собственных «случайных» решений. Серьезно, они здесь не зря.

Tanel Suurhans 26.06.2014 01:40

@TanelSuurhans Как ОП этого ответа я согласен со всеми мнениями относительно «использовать проверенные проверенные инструменты». Как раз на момент этого ответа такие инструменты еще не были основными, и похоже, что целью автора была игра в гольф. Понимание того, как работает ваш язык, так же важно, как и использование инструментов, написанных на этом языке.

Kent Fredric 26.06.2014 13:45

Решения с shuffle не будут такими уж хорошими, поскольку они будут генерировать строки с одним вхождением символа (поэтому такая строка, как "gbalgqut", будет невозможна - двойная "g")

PL J 25.06.2015 12:41

Если целью является гольф, ('a'..'z') идентичен (?a..?z); два персонажа сохранены.

aidan 18.09.2018 09:37

Я не знаю Ruby, поэтому я не могу дать вам точный синтаксис, но я бы установил постоянную строку со списком допустимых символов, а затем использовал бы оператор подстроки, чтобы выбрать из нее случайный символ.

Преимущество здесь в том, что если предполагается, что строка может вводиться пользователем, вы можете исключить легко запутанные символы, такие как l и 1 и i, 0 и O, 5 и S и т. д.

Если вы не знаете Руби, зачем вообще отвечать? : /

Ardee Aram 08.05.2013 13:59

Поскольку генерация вводимых пользователем случайных строк, которые выглядят как 0OiIlZ25S, заставляет младенца Иисуса плакать, и я хотел указать, что если случайная строка была чем-то, над чем люди должны были бы подумать, то ТОЛЬКО ВОЗМОЖНО, это могло бы быть целесообразно, мистер Холл Монитор.

nsayer 08.05.2013 20:20

Это почти так же уродливо, но, может быть, как шаг в правильном направлении?

 (1..8).map{|i| ('a'..'z').to_a[rand(26)]}.join

:) Я вообще-то извращенно люблю самый быстрый пистолет

Purfideas 18.09.2008 02:37

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

Интересно, но немного дороже в вычислительном отношении

Jeff 18.09.2008 02:49

Также нет возможности для ограниченного набора персонажей.

Kent Fredric 18.09.2008 02:58

Имейте в виду, что шестнадцатеричный дайджест возвращает только символы 0–9 и a-f.

webmat 18.09.2008 06:19

Мы использовали это в нашем коде:

class String

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

end

Максимальная поддерживаемая длина - 25 (в любом случае мы используем ее только со значением по умолчанию, поэтому проблем не возникло).

Кто-то упомянул, что "a" .. "z" неоптимально, если вы хотите полностью избежать появления оскорбительных слов. Одна из наших идей заключалась в том, чтобы удалить гласные, но вы все равно получите WTFBBQ и т. д.

Ваш подход не может возвращать повторяющиеся символы (например, uuunMoBldj) ... Это то, что нужно?

webmat 18.09.2008 06:32

Да, я полагаю, что технически это больше не случайная строка, хороший веб-сайт для поиска.

Jeff 21.09.2008 05:50

С помощью этого метода вы можете передавать произвольную длину. По умолчанию установлено 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 20.06.2013 23:31

@niels Это предложение сгенерирует строку взвешенный в пользу неповторяющихся символов. Например, если CHARS=['a','b'], то ваш метод будет генерировать "aa" или "bb" только в 33% случаев, а "ab" или "ba" - в 67% случаев. Может быть, это не проблема, но об этом стоит помнить!

Tom Lord 15.12.2017 02:27

хороший момент, @TomLord, я думаю, что на самом деле я этого не осознавал, когда размещал это предложение (хотя, должен признать, я вообще не помню, чтобы публиковал это: D)

niels 20.12.2017 00:01

Это решение генерирует строку легко читаемых символов для кодов активации; Я не хотел, чтобы люди путали 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

Спасибо, это именно то, что я хотел сделать.

anisoptera 27.04.2010 10:57

Не могли бы вы просто использовать charset.sample вместо charset.to_a[rand(charset.size)?

Tom Corelis 06.11.2011 20:54

@Tom, если вы используете Ruby 1.9 или Rails, тогда да. Обычная 1.8.7 то нет.

gtd 29.02.2012 18:54

"U" двусмысленно или это опечатка?

gtd 29.02.2012 18:57

@gtd - Ага. U и V - амбигвовы.

colinm 06.08.2013 02:54

Хотя @colinm V там.

gtd 06.08.2013 03:11

В целях безопасности вы также можете использовать SecureRandom.random_number(charset.size) вместо rand(charset.size).

gtd 10.10.2013 17:11

Я просто пытался улучшить это, добавляя строчные буквы и / или некоторые специальные символы (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 ! @ # $ % ^ & * } (первый список не включает нижний регистр, но он длиннее) Довольно интересно как это сработало.

dkniffin 15.10.2014 21:28

Я не могу вспомнить, где я это нашел, но мне кажется, что это лучший и наименее интенсивный процесс:

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

deadwards 27.01.2011 06:01

Разве здесь не пропущены 0 и 1?

sunnyrjuneja 16.03.2013 22:04

Похоже, это могло быть именно там, где я это нашел.

Travis Reeder 18.03.2013 02:01

И да, похоже, что 0 и 1 отсутствуют.

Travis Reeder 18.03.2013 02:01

0 и 1, а также O и I были намеренно пропущены, потому что эти символы неоднозначны. Если этот вид кода используется для генерации набора символов, которые пользователь должен скопировать, лучше всего исключить символы, которые может быть трудно различить вне контекста.

Andrew 28.01.2014 02:00

В Ruby 1.9 можно использовать метод выбора массива, который возвращает случайный элемент из массива.

Спасибо за внимание - однако svn.ruby-lang.org/repos/ruby/tags/v1_9_1_0/NEWS, похоже, указывает на то, что образец Array # должен использоваться в 1.9.1, а не Array # choose / choice

Andrew Grimm 31.08.2009 09:37

Почему бы не использовать SecureRandom?

require 'securerandom'
random_string = SecureRandom.hex

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

SecureRandom также имеет методы для:

  • base64
  • random_bytes
  • случайный номер

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

Это генерирует строки, которые небезопасно передавать в качестве переменных get для веб-сайтов - например, вы можете получить string = + fGH1 (+ читается как пробел) и string = fyhi / (косая черта используется для навигации)

stringo0 15.08.2010 01:58

base64 будет, но не шестнадцатеричным, как в его примере

Jeff Dickey 10.01.2011 01:39

Между прочим, это часть stdlib в версиях 1.9 и последних 1.8, так что можно просто require 'securerandom' получить этот аккуратный помощник SecureRandom :)

J-_-L 18.05.2011 01:14

@JeffDickey нет, это неправильно. SecureRandom.base64 => "7p0lfNClJwolM9BJcgG5lQ= = " как видите, в нем есть =. Шестнадцатеричный метод возвращает только 0-9a-f

Thomas 11.01.2012 15:59

Кстати, SecureRandom был удален из ActiveSupport в версии 3.2. Из журнала изменений: «Удален ActiveSupport :: SecureRandom в пользу SecureRandom из стандартной библиотеки».

Marc 21.01.2012 01:57

SecureRandom.random_number(36**12).to_s(36).rjust(12, "0") сгенерирует строку с 0-9a-z (36 символов), которая ВСЕГДА состоит из 12 символов. Измените 12 на любую желаемую длину. К сожалению, нет возможности просто получить A-Z с помощью Integer#to_s.

Gerry Shaw 18.04.2014 05:41

@ stringo0 это неверно. Если вы хотите передать +fGH1 через URL-адрес, вам просто нужно URL-кодировать его, как если бы вы использовали ЛЮБОЕ значение, которое проходит через URL-адрес: %2BfGH1

nzifnab 01.04.2015 00:08

Чтобы сгенерировать строку, содержащую только 5 буквенных символов, вы можете сделать это SecureRandom.hex.gsub(/\d/, "")[0..4]

user1491929 01.02.2016 19:14

Привет, мне нравится SecureRandom, но как мне получить строку из 6 чисел, каждая цифра находится в диапазоне 0–9. И я бы хотел, чтобы это было легко вставить в мою спецификацию контроллера Rails.

gogofan 17.05.2017 20:11

Я пробовал SecureRandom.random_number (9 ** 6), но иногда он возвращает 5 чисел, но иногда возвращает 6 чисел.

gogofan 17.05.2017 20:13

Каковы шансы получить дубликат?

dcangulo 07.11.2018 12:20

Еще один метод, который мне нравится использовать:

 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]

Jeff 12.01.2012 01:23

`pwgen 8 1`.chomp

Errno :: ENOENT: нет такого файла или каталога - pwgen 8 1

Nakilon 25.08.2011 05:41

Рубин 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 действительно хорошее!

Misha Moroshko 09.12.2010 12:45

Вы можете спросить у #sample, сколько элементов вам нужно. Например. ALPHABET.sample(10).join ... ruby-doc.org/core-2.4.0/Array.html#method-i-sample

odlp 21.11.2017 14:26

На самом деле это неправильно. sample(10) дает вам 10 уникальных образцов. Но вы хотите позволить им повторяться. Но я бы использовал Array.new(10).map для производительности

Saša Zejnilović 07.02.2018 22:46

Я хотел буквенно-цифровые символы в верхнем и нижнем регистрах. Я также перешел на использование Array.new и его блочного синтаксиса. Array.new(20) { [*'0'..'9', *'a'..'z', *'A'..'Z'].sample }.join

taylorthurlow 13.04.2018 00:35

Недавно я делал что-то подобное, чтобы сгенерировать 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; некрасиво, но работает.

Jo Liss 03.02.2011 15:43

Это отличное решение (и быстрое тоже), но время от времени оно будет выдавать строку подlength length, примерно один раз из ~ 40

Brian E 10.09.2011 16:13

@Brian E, это гарантировало бы нужные цифры: (36**(length-1) + rand(36**length)).to_s(36). 36 ** (длина-1), преобразованная в основание 36, составляет 10 ** (длина-1), что является наименьшим значением, имеющим желаемую длину цифры.

Eric Hu 08.10.2011 02:01

@EricHu Ваше решение также производит токены длиннее, чем length.

Adrien Jarthon 11.01.2013 01:09

Вот версия, всегда производящая токены желаемой длины: (36**(length-1) + rand(36**length - 36**(length-1))).to_s(36)

Adrien Jarthon 11.01.2013 01:10

Ага, +1 за краткость и отсутствие зависимостей.

Max Williams 13.03.2014 19:45

@BigBourin Ваше решение не всегда производит токены желаемой длины

justcode 20.06.2014 19:58

Это выдает мне ошибку в Rails 4 и Ruby 2.1.1: NameError: undefined local variable or method length 'для main: Object`

kakubei 14.11.2014 17:52

@BrianE У меня заканчивается терпение, прежде чем он закончится: while true do v = rand(36 ** 2); l = v.to_s(36).length; if l > 2 then puts v end end

x-yuri 19.01.2015 20:22

@ x-yuri Это потому, что вы не проверяете строки нужной длины ...

user1115652 07.07.2016 00:25

Это даст вам точную длину: Random.new.rand( 36**(length-1) ... 36**length ).to_s( 36 )

user1115652 07.07.2016 00:42

Если вам нужна скорость и точная длина, вы также можете использовать это, которое работает примерно в 20 раз быстрее, чем тот, который был в моем последнем комментарии: ( rand * (36**length - 36**(length-1) ) + 36**(length-1) ).floor.to_s( 36 ). Однако он не такой читабельный, поэтому я бы поместил его в функцию. Вероятно, самым простым было бы сгенерировать что-то, что гарантированно будет слишком длинным, а затем обрезать его.

user1115652 07.07.2016 01:55

Я использую это для генерации 6 токенов символов очень простым способом: rand(36**11).to_s(36).upcase[0,6], т.е. просто сделайте исходную строку достаточно длинной, а затем возьмите из нее любые N символов

Jan Klimo 31.01.2017 12:34

попробуйте это

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

luster 24.06.2014 23:36

Вот еще один способ:

  • Он использует безопасный генератор случайных чисел вместо rand ()
  • Может использоваться в URL-адресах и именах файлов
  • Содержит прописные, строчные буквы и цифры.
  • Есть возможность не включать неоднозначные символы I0l01

Требуется 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, «-» и «_». «=» Также используется, если заполнение истинно.

Это именно то, что я искал. Спасибо!

Dan Williams 14.10.2016 20:14

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

Проблема только в том, что каждый символ в результате уникален. Ограничивает возможные значения.

tybro0103 03.05.2012 19:04

Если этот запрос функции проходит, Ruby 1.9.x может закончить с #sample для выборки без замены и #choice для выборки с заменой.

David J. 26.06.2012 20:19

Это ошибка, думаю нужно ... [*("A".."Z")]'; ((не одинарные кавычки))

jayunit100 26.01.2016 18:30

Можете ли вы сказать мне, как я могу пройти stub, чтобы пройти rspec?

Sinscary 25.07.2016 16:12

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"

Joshua Pinter 22.08.2019 06:47

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

Что-то от Devise

Почему он заменяет строковые символы с помощью .tr('+/=lIO0', 'pqrsxyz')?

miguelcobain 09.09.2013 14:37

Специальные символы, потому что они не безопасны для URL. И l / I или O / 0, потому что их очень легко перепутать, если вы используете технику для генерации читаемых паролей пользователей.

ToniTornado 05.11.2013 17:46

Эта функция имеет некоторую предвзятость по отношению к определенным персонажам. Также для других длин (например, 16) последний символ не будет случайным. Вот способ этого избежать. SecureRandom.base64 (64) .tr ('+ / = lIO01', '') [0,16]

Shai Coleman 21.11.2014 16:59

Это основано на нескольких других ответах, но добавляет немного больше сложности:

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.

tardate 20.09.2012 15:23

Если вы используете UNIX и по-прежнему должны использовать Ruby 1.8 (без SecureRandom) без Rails, вы также можете использовать это:

random_string = `openssl rand -base64 24`

Обратите внимание, что это порождает новую оболочку, это очень медленно и может быть рекомендовано только для скриптов.

require 'securerandom'
SecureRandom.urlsafe_base64(9)

SecureRandom - отличная библиотека. Я не знал, что это было там. Спасибо.

Dogweather 28.08.2012 09:28

старая школа (и уродливая) альтернатива: Random.new.bytes(9)

tardate 20.09.2012 15:25

Кстати, urlsafe_base64 возвращает строку примерно 4/3 указанной длины. Чтобы получить строку длиной ровно n символов, попробуйте n=9 ; SecureRandom.urlsafe_base64(n)[0..n-1]

tardate 20.09.2012 15:43

Вот один простой код для случайного пароля длиной 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/…

Breedly 16.02.2017 19:28

При необходимости создайте пустую строку или префикс:

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.

Omar Ali 27.03.2016 21:31

@OmarAli, ты ошибаешься. Согласно документации Ruby в случае .hex это 2n. Документация: SecureRandom.hex генерирует случайную шестнадцатеричную строку. Аргумент п указывает длину в байтах генерируемого случайного числа. Длина результирующей шестнадцатеричной строки вдвое больше п.

Rihards 10.02.2020 14:47

Вот решение, которое является гибким и допускает дублирование:

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

Отличная жемчужина. Спасибо :)

Mirko Akov 07.01.2014 03:07

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 и др.

От: "Сгенерировать псевдослучайную строку A-Z, 0-9".

Вот однострочный простой код для случайной строки длиной 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 раз проще подобрать грубую силу).

Ben Carlsson 07.07.2017 22:26

Это решение требует внешней зависимости, но кажется красивее другого.

  1. Установить гем притворщик
  2. Faker::Lorem.characters(10) # => "ang9cbhoa8"

Передача числа с 1-м аргументом символов устарела. Вместо этого используйте аргумент ключевого слова, например characters(number: ...). Faker::Lorem.characters(number: 10) # => "ang9cbhoa8"

Mr. Rene 12.02.2021 06:02

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

Kyle Tolle 26.07.2016 01:13

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.

Другие вопросы по теме