Создать регулярное выражение из строки, хранящейся в переменной, с экранированными символами (Ruby)

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

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

Regexp.new("\d") => /d/

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

Regexp.new('\d') => /\d/

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

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

(Я бы хотел использовать функцию строковой интерполяции двойных кавычек)

ex.:

email_pattern = "/[a-z]*\.com"
whole_pattern = "to: #{email_pattern}"
Regexp.new(whole_pattern)

Для лучшей читаемости я бы не хотел экранировать escape-символы.

"\\d"

person Victor Marconi    schedule 06.11.2012    source источник
comment
Почему бы вам не использовать одинарные кавычки для самой внутренней строки (email_pattern) и двойные кавычки для включающих строк (whole_pattern)?   -  person Frank Schmitt    schedule 06.11.2012


Ответы (2)


Проблема в том, что вы получаете совершенно разные строки, в зависимости от того, используете ли вы одинарные или двойные кавычки:

"\d".chars.to_a
#=> ["d"]

'\d'.chars.to_a
#=> ["\\", "d"]

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

"\d" == "d"
#=> true

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

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

regex1 = /\d/
#=> /\d/

regex2 = /foobar/
#=> /foobar/

Затем вы можете создать окончательное регулярное выражение с интерполяцией в стиле #{} вместо создания источника регулярного выражения из строк:

regex3 = /#{regex1} #{regex2}/
#=> /(?-mix:\d) (?-mix:foobar)/

Отражая ваш пример, это будет означать:

email_regex = /[a-z]*\.com/
whole_regex = /to: #{email_regex}/
#=> /to: (?-mix:[a-z]*\.com)/

Вы также можете найти Regexp#escape интересным. (см. документацию)

Если вы столкнетесь с дальнейшими проблемами экранирования (с косой чертой), вы также можете использовать альтернативный буквальный синтаксис Regexp с %r{<your regex here>}, в котором вам не нужно экранировать символ /. Например:

%r{/}
#=> /\//

Однако от обратной косой черты \ с помощью \\ никуда не деться.

person Patrick Oscity    schedule 06.11.2012

Либо создайте свою строку с одинарными кавычками:

 s = '\d'
 r = Regexp.new(s)

или процитируйте обратную косую черту:

 s = "\\d"
 r = Regexp.new(s)

Оба должны работать.

person Frank Schmitt    schedule 06.11.2012
comment
Спасибо за ответ. Я рассмотрел эти предложения, но не хотел бы использовать escape-символ. Я чувствую, что это становится нечитаемым, как только регулярное выражение растет. - person Victor Marconi; 06.11.2012
comment
Можете ли вы создать строку в одинарных кавычках? - person Frank Schmitt; 06.11.2012