module Random::Formatter
Форматирование случайных чисел.
Форматирует сгенерированные случайные числа различными способами. При подключении 'random/formatter' к пустому основному модулю Random::Formatter добавляется несколько методов, благодаря чему они становятся доступны как методы экземпляра и модуля Random.
Стандартная библиотека SecureRandom также расширяется этим модулем, и описанные ниже методы доступны в ней как методы модуля.
Примеры
Сгенерировать случайные шестнадцатеричные строки:
require 'random/formatter' prng = Random.new prng.hex(10) #=> "52750b30ffbc7de3b362" prng.hex(10) #=> "92b15d6c8dc4beb5f559" prng.hex(13) #=> "39b290146bea6ce975c37cfc23" # or just Random.hex #=> "1aed0c631e41be7f77365415541052ee"
Сгенерировать случайные строки в кодировке base64:
prng.base64(10) #=> "EcmTPZwWRAozdA==" prng.base64(10) #=> "KO1nIU+p9DKxGg==" prng.base64(12) #=> "7kJSM/MzBJI+75j8" Random.base64(4) #=> "bsQ3fQ=="
Сгенерировать случайные двоичные строки:
prng.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
prng.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
Random.random_bytes(6) #=> "\xA1\xE6Lr\xC43"
Сгенерировать буквенно-цифровые строки:
prng.alphanumeric(10) #=> "S8baxMJnPl" prng.alphanumeric(10) #=> "aOxAg8BAJe" Random.alphanumeric #=> "TmP9OsJHJLtaZYhP"
Сгенерировать UUID:
prng.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594" prng.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab" Random.uuid #=> "f14e0271-de96-45cc-8911-8910292a42cd"
Все методы также доступны в стандартной библиотеке SecureRandom:
SecureRandom.hex #=> "05b45376a30c67238eb93b16499e50cf"
Сгенерировать случайное число в заданном диапазоне, как это делает Random
prng.random_number #=> 0.5816771641321361 prng.random_number(1000) #=> 485 prng.random_number(1..6) #=> 3 prng.rand #=> 0.5816771641321361 prng.rand(1000) #=> 485 prng.rand(1..6) #=> 3
Константы
- ALPHANUMERIC
-
Список символов по умолчанию для
alphanumeric.
Открытые методы экземпляра
# File lib/random/formatter.rb, line 367 def alphanumeric(n = nil, chars: ALPHANUMERIC) n = 16 if n.nil? choose(chars, n) end
Сгенерировать случайную буквенно-цифровую строку.
Аргумент n задает длину генерируемой буквенно-цифровой строки в символах. Аргумент chars задает список символов, из которых состоит результат.
Если n не задан или равен nil, предполагается значение 16. В будущем оно может быть увеличено.
Результат может содержать символы A–Z, a–z и 0–9, если не задан аргумент chars.
require 'random/formatter' Random.alphanumeric #=> "2BuBuLf3WfSKyQbR" # or prng = Random.new prng.alphanumeric(10) #=> "i6K93NdqiH" Random.alphanumeric(4, chars: [*"0".."9"]) #=> "2952" # or prng = Random.new prng.alphanumeric(10, chars: [*"!".."/"]) #=> ",.,++%/''."
# File lib/random/formatter.rb, line 114
def base64(n=nil)
[random_bytes(n)].pack("m0")
end Сгенерировать случайную строку в кодировке base64.
Аргумент n задает длину генерируемого случайного числа в байтах. Длина результирующей строки составляет примерно 4/3 от n.
Если n не задан или равен nil, предполагается значение 16. В будущем оно может быть увеличено.
Результат может содержать символы A–Z, a–z, 0–9, «+», «/» и «=».
require 'random/formatter' Random.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A==" # or prng = Random.new prng.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
Определение base64 см. в RFC 3548.
# File lib/random/formatter.rb, line 92
def hex(n=nil)
random_bytes(n).unpack1("H*")
end Сгенерировать случайную шестнадцатеричную строку.
Аргумент n задает длину генерируемого случайного числа в байтах. Длина результирующей шестнадцатеричной строки вдвое больше n.
Если n не задан или равен nil, предполагается значение 16. В будущем оно может быть увеличено.
Результат может содержать символы 0–9 и a–f.
require 'random/formatter' Random.hex #=> "eb693ec8252cd630102fd0d0fb7c3485" # or prng = Random.new prng.hex #=> "91dc3bfb4de5b11d029d376634589b61"
Генерирует форматированное случайное число из необработанных случайных байтов. См. Random#rand.
# File lib/random/formatter.rb, line 71 def random_bytes(n=nil) n = n ? n.to_int : 16 gen_random(n) end
Сгенерировать случайную двоичную строку.
Аргумент n задает длину результирующей строки.
Если n не задан или равен nil, предполагается значение 16. В будущем оно может быть увеличено.
Результат может содержать любой байт: «x00» — «xff».
require 'random/formatter' Random.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6" # or prng = Random.new prng.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
static VALUE
rand_random_number(int argc, VALUE *argv, VALUE obj)
{
rb_random_t *rnd = try_get_rnd(obj);
VALUE v = rand_random(argc, argv, obj, rnd);
if (NIL_P(v)) v = rand_random(0, 0, obj, rnd);
else if (!v) invalid_argument(argv[0]);
return v;
} Генерирует форматированное случайное число из необработанных случайных байтов. См. Random#rand.
# File lib/random/formatter.rb, line 145
def urlsafe_base64(n=nil, padding=false)
s = [random_bytes(n)].pack("m0")
s.tr!("+/", "-_")
s.delete!("=") unless padding
s
end Сгенерировать случайную строку base64, безопасную для URL.
Аргумент n задает длину генерируемого случайного числа в байтах. Длина результирующей строки составляет примерно 4/3 от n.
Если n не задан или равен nil, предполагается значение 16. В будущем оно может быть увеличено.
Логический аргумент padding задает дополнение. Если он равен false или nil, дополнение не создается. В противном случае создается дополнение. По умолчанию дополнение не создается, поскольку «=» может использоваться как разделитель URL.
Результат может содержать символы A–Z, a–z, 0–9, «-» и «_». Символ «=» также используется, если padding имеет значение true.
require 'random/formatter' Random.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg" # or prng = Random.new prng.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg" prng.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ==" prng.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="
Определение URL-безопасного base64 см. в RFC 3548.
# File lib/random/formatter.rb, line 169
def uuid
ary = random_bytes(16)
ary.setbyte(6, (ary.getbyte(6) & 0x0f) | 0x40)
ary.setbyte(8, (ary.getbyte(8) & 0x3f) | 0x80)
ary.unpack("H8H4H4H4H12").join(?-)
end Сгенерировать случайный UUID версии 4 (универсальный уникальный идентификатор).
require 'random/formatter' Random.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594" Random.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab" # or prng = Random.new prng.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
UUID версии 4 является полностью случайным (за исключением версии). Он не содержит значимой информации, такой как MAC-адреса, временные метки и т. д.
Результат содержит 122 случайных бита (15,25 случайных байта).
Подробные сведения о UUIDv4 см. в RFC9562.
# File lib/random/formatter.rb, line 246
def uuid_v7(extra_timestamp_bits: 0)
case (extra_timestamp_bits = Integer(extra_timestamp_bits))
when 0 # min timestamp precision
ms = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
rand = random_bytes(10)
rand.setbyte(0, rand.getbyte(0) & 0x0f | 0x70) # version
rand.setbyte(2, rand.getbyte(2) & 0x3f | 0x80) # variant
"%08x-%04x-%s" % [
(ms & 0x0000_ffff_ffff_0000) >> 16,
(ms & 0x0000_0000_0000_ffff),
rand.unpack("H4H4H12").join("-")
]
when 12 # max timestamp precision
ms, ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
.divmod(1_000_000)
extra_bits = ns * 4096 / 1_000_000
rand = random_bytes(8)
rand.setbyte(0, rand.getbyte(0) & 0x3f | 0x80) # variant
"%08x-%04x-7%03x-%s" % [
(ms & 0x0000_ffff_ffff_0000) >> 16,
(ms & 0x0000_0000_0000_ffff),
extra_bits,
rand.unpack("H4H12").join("-")
]
when (0..12) # the generic version is slower than the special cases above
rand_a, rand_b1, rand_b2, rand_b3 = random_bytes(10).unpack("nnnN")
rand_mask_bits = 12 - extra_timestamp_bits
ms, ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
.divmod(1_000_000)
"%08x-%04x-%04x-%04x-%04x%08x" % [
(ms & 0x0000_ffff_ffff_0000) >> 16,
(ms & 0x0000_0000_0000_ffff),
0x7000 |
((ns * (1 << extra_timestamp_bits) / 1_000_000) << rand_mask_bits) |
rand_a & ((1 << rand_mask_bits) - 1),
0x8000 | (rand_b1 & 0x3fff),
rand_b2,
rand_b3
]
else
raise ArgumentError, "extra_timestamp_bits must be in 0..12"
end
end Сгенерировать случайный UUID версии 7 (универсальный уникальный идентификатор).
require 'random/formatter' Random.uuid_v7 # => "0188d4c3-1311-7f96-85c7-242a7aa58f1e" Random.uuid_v7 # => "0188d4c3-16fe-744f-86af-38fa04c62bb5" Random.uuid_v7 # => "0188d4c3-1af8-764f-b049-c204ce0afa23" Random.uuid_v7 # => "0188d4c3-1e74-7085-b14f-ef6415dc6f31" # |<--sorted-->| |<----- random ---->| # or prng = Random.new prng.uuid_v7 # => "0188ca51-5e72-7950-a11d-def7ff977c98"
UUID версии 7 начинается с 48 младших бит 64-битной временной метки Unix (миллисекунды с начала эпохи), а оставшиеся биты заполняются случайными данными, за исключением битов версии и варианта.
Это позволяет сортировать UUID версии 7 по времени создания. UUID, упорядоченные по Time, можно использовать для повышения локальности индекса базы данных для новых записей, что может обеспечить значительный прирост производительности по сравнению со вставкой случайных данных.
Результат содержит 74 случайных бита (9,25 случайного байта).
Обратите внимание, что этот метод нельзя сделать воспроизводимым, поскольку его результат включает не только случайные биты, но и временную метку.
Подробные сведения о UUIDv7 см. в RFC9562.
Монотонность
По умолчанию UUIDv7 имеет точность до миллисекунд, поэтому несколько UUID, созданных в течение одной миллисекунды, не выдаются в строго возрастающем порядке. Для создания UUID, упорядоченных по времени с точностью до долей миллисекунды, можно добавить до 12 дополнительных бит временной метки с помощью extra_timestamp_bits. Дополнительная точность временной метки достигается за счет случайных битов. Значение extra_timestamp_bits: 12 обеспечивает точность ~244 нс, но оставляет только 62 случайных бита (7,75 случайного байта).
prng = Random.new
Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 12) }
# =>
["0188d4c7-13da-74f9-8b53-22a786ffdd5a",
"0188d4c7-13da-753b-83a5-7fb9b2afaeea",
"0188d4c7-13da-754a-88ea-ac0baeedd8db",
"0188d4c7-13da-7557-83e1-7cad9cda0d8d"]
# |<--- sorted --->| |<-- random --->|
Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 8) }
# =>
["0188d4c7-3333-7a95-850a-de6edb858f7e",
"0188d4c7-3333-7ae8-842e-bc3a8b7d0cf9", # <- out of order
"0188d4c7-3333-7ae2-995a-9f135dc44ead", # <- out of order
"0188d4c7-3333-7af9-87c3-8f612edac82e"]
# |<--- sorted -->||<---- random --->|
Любой перевод системных часов назад нарушит монотонность. UUIDv7 основан на UTC, где не учитываются високосные секунды и возможен перевод часов назад. Чтобы этого избежать, системные часы можно синхронизировать с NTP-сервером, настроенным на использование метода «leap smear». Для синхронизации распределенных узлов также потребуется NTP или PTP.
Счетчики и другие механизмы для обеспечения более строгих гарантий монотонности не реализованы. Приложениям с более строгими требованиями следует руководствоваться разделом 6.2 спецификации.
Закрытые методы экземпляра
# File lib/random/formatter.rb, line 312
def choose(source, n)
size = source.size
m = 1
limit = size
while limit * size <= 0x100000000
limit *= size
m += 1
end
result = ''.dup
while m <= n
rs = random_number(limit)
is = rs.digits(size)
(m-is.length).times { is << 0 }
result << source.values_at(*is).join('')
n -= m
end
if 0 < n
rs = random_number(limit)
is = rs.digits(size)
if is.length < n
(n-is.length).times { is << 0 }
else
is.pop while n < is.length
end
result.concat source.values_at(*is).join('')
end
result
end Сгенерировать строку, случайным образом выбирая символы из исходного массива.
Аргумент source задает массив символов, из которых генерируется строка. Аргумент n задает длину генерируемой строки в символах.
Результат может содержать любые символы из исходного массива.
require 'random/formatter' prng.choose([*'l'..'r'], 16) #=> "lmrqpoonmmlqlron" prng.choose([*'0'..'9'], 5) #=> "27309"
# File lib/random/formatter.rb, line 294
def gen_random(n)
self.bytes(n)
end Внутренний интерфейс к Random; генерирует случайные данные длиной n байт.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.