Поиск и редактирование нескольких совпадений регулярных выражений в одной строке

Я хочу добавить уценку к ключевым фразам на вики-странице (gollum), которая будет ссылаться на соответствующую вики-страницу в форме:

This is the key phrase.

становится

This is the [[key phrase|Glossary#key phrase]].

У меня есть список ключевых фраз, таких как:

keywords = ["golden retriever", "pomeranian", "cat"]

И документ:

Sue has 1 golden retriever. John has two cats.
Jennifer has one pomeranian. Joe has three pomeranians.

Я хочу перебрать каждую строку и найти каждое совпадение (которое еще не является ссылкой) для каждого ключевого слова. Моя текущая попытка выглядит так:

File.foreach(target_file) do |line|
    glosses.each do |gloss|
        len = gloss.length
        # Create the regex. Avoid anything that starts with [
        # or (, ends with ] or ), and ignore case.
        re = /(?<![\[\(])#{gloss}(?![\]\)])/i
        # Find every instance of this gloss on this line.
        positions = line.enum_for(:scan, re).map {Regexp.last_match.begin(0) }
        positions.each do |pos|
            line.insert(pos, "[[")
            # +2 because we just inserted 2 ahead.
            line.insert(pos+len+2, "|#{page}\##{gloss}]]")
        end
    end
    puts line
end

Однако это столкнется с проблемой, если в одной строке есть два совпадения для одной и той же ключевой фразы. Поскольку я вставляю элементы в строку, позиция, которую я нашел для каждого совпадения, не является точной после первого. Я знаю, что могу каждый раз корректировать размер своих вставок, но, поскольку мои вставки имеют разный размер для каждого глянца, это кажется самым грубым, хакерским решением.

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


person Nich Del    schedule 30.06.2016    source источник
comment
Нравится это?   -  person Bryce Drew    schedule 01.07.2016
comment
@BryceDrew Спасибо за ответ. Это кажется в основном правильным, но он не делает никаких утверждений вперед или назад, что помешало бы добавлению ссылки к существующим ссылкам. В идеале мой скрипт должен запускаться в документе после его обновления вручную, чтобы добавлять новые ссылки (без вмешательства в существующие).   -  person Nich Del    schedule 01.07.2016
comment
@BryceDrew Я нашел свой ответ, в основном на вашем примере. Большое спасибо!   -  person Nich Del    schedule 01.07.2016


Ответы (1)


Посмотрев онлайн-версию Python от @BryceDrew, я понял, что ruby, вероятно, также может заполнить совпадение. Теперь у меня есть гораздо более краткое и быстрое решение.

Во-первых, мне нужно было сделать регулярные выражения моих глосс:

glosses.push(/(?<![\[\(])#{gloss}(?![\]\)])/i)

Примечание. Большая часть этого регулярного выражения представляет собой утверждения с опережением и просмотром назад, чтобы предотвратить перехват фразы, которая уже является частью ссылки.

Затем мне нужно было создать объединение все они:

re = Regexp.union(glosses)

После этого достаточно выполнить gsub. в каждой строке и заполнив мои совпадения:

File.foreach(target_file) do |line|
  line = line.gsub(re) {|match| "[[#{match}|Glossary##{match.downcase}]]"}
  puts line
end
person Nich Del    schedule 30.06.2016
comment
Вы, вероятно, захотите поместить границу слова по обе стороны от вашего регулярного выражения, чтобы избежать захвата, например. катапульта для кота. Примерно так: re = /\b#{Regexp.union(glosses)}\b/. - person Jordan Running; 01.07.2016
comment
@Jordan Я думал об этом, но я хочу уловить множественное число и окончания глаголов, так что это обмен между ложноотрицательными и ложноположительными. - person Nich Del; 01.07.2016