Замена частичных регулярных выражений на Ruby

Я хочу преобразовать следующий текст

This is a ![foto](foto.jpeg), here is another ![foto](foto.png)

на

This is a ![foto](/folder1/foto.jpeg), here is another ![foto](/folder2/foto.png)

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

Я хотел бы сделать это с помощью String#gsub в его блочной версии. В настоящее время мой код выглядит так:

re = /![.*?]((.*?))/

rel_content = content.gsub(re) do |path|
    real_path(path)
end

в проблема с этим регулярным выражением заключается в том, что оно будет соответствовать просто foto.jpeg. Я также пробовал другие regexen, как (?>![.*?]()(.*?)(?>)) но безрезультатно.

мой текущий обходной путь-разделить путь и собрать его позже.

есть ли регулярное выражение Ruby, которое соответствует только пути внутри скобок, а не всем контекстным необходимым символам?

обновление после ответов: основная проблема здесь заключается в том, что Руби regexen нет никакого способа, чтобы указать нулевой ширины lookbehinds. Самое общее решение-сгруппировать то, что часть regexp до и после реальной совпадающей части, т. е. /(pre)(matching-part)(post)/, а затем восстановите полную строку.

в этом случае решение должно быть

re = /(![.*?]()(.*?)())/

rel_content = content.gsub(re) do
     + real_path() + 
end

3 ответов


быстрое решение (отрегулируйте по мере необходимости):

s = 'This is a ![foto](foto.jpeg)'

s.sub!(/!(\[.*?\])\((.*?)\)/, '(/folder1/)' )

p s  # This is a [foto](/folder1/foto.jpeg)

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

str = "This is a ![foto](foto.jpeg), here is another ![foto](foto.png)"

str.gsub(/\!\[[^\]]*\]\(([^)]*)\)/) do |image|
  image.gsub(/(?<=\()(.*)(?=\))/) do |link|
    "/a/new/path/" + link
  end
end

#=> "This is a ![foto](/a/new/path/foto.jpeg), here is another ![foto](/a/new/path/foto.png)"

Я немного изменил первое регулярное выражение, но вы можете использовать то же самое, что и раньше. image - это выражение изображения как ![foto](foto.jpeg) и link - это просто путь, как foto.jpeg.

[EDIT] уточнение: у Ruby есть lookbehinds (и они используются в моем ответе):

вы можете создать lookbehinds С (?<=regex) и (?<!regex) для отрицательных, где regex - произвольное выражение regex при соблюдении следующих условий. Выражения регулярных выражений в lookbehinds они должны быть фиксированной шириной из-за ограничений на реализацию регулярных выражений, что означает, что они не могут включать выражения с неизвестным количеством повторений или чередований с различными вариантами ширины. Если вы попытаетесь сделать это, вы получите ошибку. (Ограничение не распространяется на заглядывание вперед, хотя).

в вашем случае [foto] часть имеет переменную ширину (foto может быть любой строкой), поэтому он не может перейти в lookbehind из-за вышеизложенного. Однако lookbehind-это именно то, что нам нужно, поскольку это совпадение нулевой ширины, и мы используем это во втором регулярном выражении, которому нужно только беспокоиться о (фиксированной длине) обязательных открытых скобках.

очевидно, что вы можете поставить real_path отсюда, но я просто хотел проверить пример.

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


в блоке, используйте для доступа к первой группе захвата ( для второго и так далее).

из документации:

в блочной форме текущая строка соответствия передается в качестве параметра и переменных, таких как $1, $2, $`, $&, и $ ' будет установлен соответствующим образом. Значение, возвращаемое блоком, будет заменено на совпадение при каждом вызове.