частичная замена gsub - PullRequest
19 голосов
/ 23 сентября 2008

Я хотел бы заменить только группу в скобках в этом выражении:

my_string.gsub(/<--MARKER_START-->(.)*<--MARKER_END-->/, 'replace_text')

так что я получаю: <--MARKER_START-->replace_text<--MARKER_END-->

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

Ответы [ 2 ]

16 голосов
/ 23 сентября 2008

Вы можете сделать это с помощью предварительных утверждений нулевой ширины и утверждений .

Это регулярное выражение должно работать в ruby ​​1.9, perl и многих других местах:

Примечание: ruby ​​1.8 поддерживает только прогнозные утверждения. Чтобы сделать это должным образом, вам нужны как упреждающие, так и опережающие взгляды.

 s.gsub( /(?<=<--MARKER START-->).*?(?=<--MARKER END-->)/, 'replacement text' )

То, что происходит в ruby ​​1.8, это ?<=, вызывающее его аварийное завершение, потому что оно не понимает утверждение о проверке. Для этой части вам придется использовать обратную ссылку - например, Грейг Хьюгилл упоминает

так что вы получите

 s.gsub( /(<--MARKER START-->).*?(?=<--MARKER END-->)/, '\1replacement text' )

ОБЪЯСНЕНИЕ ПЕРВЫЙ:

Я заменил (.)* в середине вашего регулярного выражения на .*? - это не жадный. Если у вас нет нежадных, то ваше регулярное выражение будет пытаться сопоставить столько, сколько может - если у вас есть 2 маркера на одной строке, это пойдет не так. Это лучше всего иллюстрируется на примере:

"<b>One</b> Two <b>Three</b>".gsub( /<b>.*<\/b>/, 'BOLD' )
=> "BOLD"

Что мы на самом деле хотим:

"<b>One</b> Two <b>Three</b>".gsub( /<b>.*?<\/b>/, 'BOLD' )
=> "BOLD Two BOLD"

ОБЪЯСНЕНИЕ ВТОРОЕ:

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

Что на самом деле означает «упреждающее утверждение», так это «Только совпадение, если то, что мы ищем, сопровождается другими вещами.

Например, совпадать только с цифрой, если за ней следует F.

"123F" =~ /\d(?=F)/ # will match the 3, but not the 1 or the 2

Что на самом деле означает «нулевая ширина», так это «учитывайте« с последующим »в нашем поиске, но не учитывайте его как часть соответствия при выполнении замены или группировки или тому подобного. Используя тот же пример 123F, если мы не использовали утверждение предпросмотра, а вместо этого просто сделаем это:

"123F" =~ /\dF/ # will match 3F, because F is considered part of the match

Как видите, это идеально подходит для проверки нашего <--MARKER END-->, но нам нужно для <--MARKER START--> возможность сказать: "Только совпадение, если то, что мы ищем, СЛЕДУЕТ за этим другим". Это называется предварительным утверждением, которого ruby ​​1.8 не имеет по какой-то странной причине ..

Надеюсь, что имеет смысл: -)

PS: Зачем использовать косвенные утверждения вместо просто обратных ссылок? Если вы используете lookahead, вы на самом деле не заменяете биты <--MARKER-->, а только содержимое. Если вы используете обратные ссылки, вы заменяете всю партию. Я не знаю, сильно ли это сказывается на производительности, но с точки зрения программирования кажется, что это правильно, так как мы вообще не хотим заменять маркеры.

14 голосов
/ 23 сентября 2008

Вы можете сделать что-то вроде этого:

my_string.gsub(/(<--MARKER_START-->)(.*)(<--MARKER_END-->)/, '\1replace_text\3')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...