Лучший способ преобразовать строковый диапазон Ruby в объект Range - PullRequest
20 голосов
/ 10 сентября 2008

У меня есть некоторый Ruby-код, который принимает даты в командной строке в формате:

-d 20080101,20080201..20080229,20080301

Это означает, что я хочу работать для всех дат между 20080201 и 20080229 (включительно) и других дат, присутствующих в списке.

Учитывая, что я могу получить строку 20080201..20080229, что является лучшим способом преобразовать это в экземпляр Range. В настоящее время я использую eval, но мне кажется, что должен быть лучший способ.

@ Purfideas Я как бы искал более общий ответ для преобразования любой строки типа int..int в Range, я думаю.

Ответы [ 8 ]

33 голосов
/ 14 июля 2010
Range.new(*self.split("..").map(&:to_i))
14 голосов
/ 10 сентября 2008

Но тогда просто сделайте

ends = '20080201..20080229'.split('..').map{|d| Integer(d)}
ends[0]..ends[1]

в любом случае я не рекомендую eval, из соображений безопасности

7 голосов
/ 10 сентября 2008

Inject без аргументов хорошо работает для двухэлементных массивов:

rng='20080201..20080229'.split('..').inject { |s,e| s.to_i..e.to_i }

Конечно, это можно сделать общим

class Range
  def self.from_ary(a)
    a.inject{|s,e| s..e }
  end
end

rng = Range.from_ary('20080201..20080229'.split('..').map{|s| s.to_i})
rng.class  # => Range
2 голосов
/ 10 сентября 2008

Если вы хотите, чтобы диапазон корректировался в течение месяцев и т. Д., Попробуйте

require 'date'

ends = '20080201..20080229'.split('..').map{|d| Date.parse(d)}
(ends[0]..ends[1]).each do |d|
  p d.day
end
1 голос
/ 29 апреля 2015

Для этого есть драгоценный камень . Использует регулярное выражение для проверки строки (без страха внедрения SQL), а затем eval.

0 голосов
/ 09 января 2018

Здесь предположим, что вы хотите сохранить хеш как системное значение и извлечь его в любой модели. Ключ хеша будет иметь значение диапазона.

hash_1 = {1..5 => 'a', 6..12 => 'b', 13..67 => 'c', 68..9999999 => 'd'}

Затем создайте системную константу со значением hash_1.to_json. .to_json преобразует ваш хеш-объект в JSON-объект. Теперь внутри кода создайте новый хэш hash_2,

JSON.parse(SystemConstant.get('Constant_name')).each{|key,val| temp_k=key.split('..').map{|d| Integer(d)}; hash_2[temp_k[0]..temp_k[1]] = val}

Новый hash_2 будет требуемым hash_1

0 голосов
/ 18 ноября 2014

Если мы сделаем это как

v= "20140101..20150101"
raise "Error: invalid format: #{v}" if /\d{8}\.\.\d{8}/ !~ v
r= eval(v)

и у атакующего есть способ обойти проверку повышения (просто посредством манипулирования средой выполнения, чтобы отключить исключения), тогда мы можем получить опасное значение, которое потенциально разрушит вселенную.

Таким образом, для уменьшения векторов атаки мы проверяем формат, затем выполняем синтаксический анализ вручную, затем проверяем результаты

v= "20140101..20150101"
raise "Error: invalid format: #{v}" if /\d{8}\.\.\d{8}/ !~ v
r= Range.new(*v.split(/\.\./).map(&:to_i))
raise "Error: invalid range: #{v}" if r.first> r.last
0 голосов
/ 28 января 2014

Объединив ответ @Purfideas с другим ответом где-то в StackOverflow, я решил эту проблему, также окружив код проверкой ввода, поэтому единственное, что используется, - это допустимое перечисляемое значение

if !value[/^[0-9]+\.\.[0-9]+$/].nil?
    ends = value.split('..').map{|d| Integer(d)}
    value = ends[0]..ends[1]
end

Это по существу переписывает ваше строковое значение в перечисляемое значение. Это удобно, если вы добавляете перечисляемое поле в файл конфигурации yaml.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...