2013-01-08 4 views
3

У меня есть два типа струн. Если строка содержит foo сначала и позже bar, ее не следует трогать. Если он содержит только bar, то bar, следует заменить на qux.Отрицательный взгляд сзади с чем-то посередине

  • "sometext foo someothetext bar somethirdtext" не должны быть затронуты
  • "sometext bar someothetext" =>"sometext qux someothetext"

Похоже, мне нужно использовать отрицательный взгляд назад, но я не могу заставить его работать правильно. В настоящее время, у меня есть выражение:

str.gsub! (/(?<!foo)(.*)bar/), '\1qux' 

Однако он заменяет bar к qux в обеих строках. У меня такое чувство, что .* завинчивает вещи. Я не смог найти внешний пример, где внешний вид группы не предшествует соответствующей группе сразу.

ответ

0

Если вы могли бы использовать переменные длины, вы могли бы просто заменить совпадения /(?<!foo.*)bar/ на 'qux'.

Без поддержки переменных длины вы можете реально использовать lookbehind, потому что вы не можете знать позицию, чтобы запустить lookbehind, чтобы найти foo. Вот как вы можете сделать это с помощью опережающего просмотра:

str.gsub! (/\A(((?!foo).)*)bar/m), '\1qux' 

Объяснение:

\A   # match at the start of the string 
(   # start capture group 1 
    (   # start capture group 2, repeat 0 or more times 
    (?!foo)  # fail if 'foo' can match here 
    .   # match a single character 
)*   # end capture group 2 
)   # end capture group 1 
bar   # match 'bar' 

Это выполняет отрицательный предпросмотр (?!foo) на каждого персонажа, пока мы не совпадают bar, поэтому он не будет соответствовать строки, где foo приходит до bar.

Якорь к началу строки необходим, потому что если вы можете начать посередине строки, он только начнет матч сразу после f в foo. Многострочная опция используется так, чтобы символ . соответствовал разрыву строк, не уверен, что это необходимо для того, что вы делаете.

0

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

if str.include? "foo" 
    str = str.slice(0, str.index("foo")-1).sub("bar","qux") + str.slice(str.index("foo")-1, str.length) 
else 
    str = str.sub("bar","qux") 
end 

Это будет заменить любой «бар» s перед первым экземпляром «Foo», а затем лавировать на остальной части строки

0

Может быть, вам может использовать flag для отслеживания того, был ли предшествующий foo.

flag = false 
"sometext foo someothetext bar somethirdtext" 
.gsub(/(foo)|bar/){flag = true if $1; flag ? $& : "qux"} 
# => "sometext foo someothetext bar somethirdtext" 

flag = false 
"sometext bar someothetext" 
.gsub(/(foo)|bar/){flag = true if $1; flag ? $& : "qux"} 
# => "sometext qux someothetext" 
Смежные вопросы