Замена частичных совпадений регулярных выражений на месте Ruby - PullRequest
7 голосов
/ 11 декабря 2011

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

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)

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

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

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

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

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

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

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

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

В этом случае решение будет

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

rel_content = content.gsub(re) do
    $1 + real_path($2) + $3
end

Ответы [ 4 ]

6 голосов
/ 12 декабря 2011

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

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

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

p s  # This is a [foto](/folder1/foto.jpeg)
4 голосов
/ 12 декабря 2011

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

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.

[РЕДАКТИРОВАТЬ] Уточнение: у Руби есть взгляды (и они используются в моем ответе):

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

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

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

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

3 голосов
/ 12 декабря 2011

В вашем блоке используйте $1 для доступа к первой группе захвата ($2 для второй и т. Д.).

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

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

1 голос
/ 14 марта 2019

В качестве примечания , некоторые люди считают, что '\ 1' неуместно в ситуациях, когда сопоставляется неподтвержденное количество символов.Например, если вы хотите сопоставить и изменить средний контент, как вы можете защитить символы с обеих сторон?

Это просто.Поставьте скобку вокруг чего-то еще.

Например, я надеюсь заменить a-ruby-porgramming-book-531070.png на a-ruby-porgramming-book.png.Удалить контекст между последним "-" и последним ".".

Я могу использовать /.*(-.*?)\./ match -531070.Теперь, как я должен заменить его?Обратите внимание, что все остальное не имеет определенного формата.

Ответ заключается в том, чтобы заключить в скобки что-то другое, а затем защитить их:

"a-ruby-porgramming-book-531070.png".sub(/(.*)(-.*?)\./, '\1.') 
# => "a-ruby-porgramming-book.png"

Если вы хотите добавить что-то перед соответствующим контентом, вы можетеиспользовать:

"a-ruby-porgramming-book-531070.png".sub(/(.*)(-.*?)\./, '\1-2019\2.')
# => "a-ruby-porgramming-book-2019-531070.png"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...