Сопоставить текст между двумя словами (слова не повторяются в сопоставленном тексте)

У меня есть следующее как мой вход,

вход

Random Line 1
Random Line 2
From: person1@example.com
Date: 01-01-2011
To: friend@example.com
   Subject: One
Random Line 3
Random Line 4
From: person2@example.com
   Subject: Two
Random Line 5
From: person3@example.com
   Subject: Three
This is the end

Ниже приведен мой ожидаемый сопоставленный текст,

Ожидаемый результат

From: person2@example.com
   Subject: Two

Примечание. Между From: person2@example.com и Subject: Two может быть ноль или несколько строк.

Я пытался с регулярным выражением,

/(From.*?Subject:\s*Two)/m

Вышеуказанное регулярное выражение совпадает с первым From. Может ли кто-нибудь помочь мне в сопоставлении ожидаемого результата? Заранее спасибо.

4 ответа

Решение

Добавлять .* перед вашим регулярным выражением, чтобы получить только ожидаемые две строки.

.*(From.*?Subject:\s*Two)

Из-за жадного квантификатора *двигатель regex соответствует последнему From строка (то есть та, что перед строкой, которая содержит строку Two). Тогда из строки From до струны Two записывается в группу (используется не жадный квантификатор. поэтому он делает кратчайшее совпадение).

DEMO

Это еще один способ:

Код

text.scan(/.*(From:.*?\n).*(Subject: Two)/m).join

пример

text = <<_
Line 1
From: person1@example.com
To: friend@example.com
   Subject: One
Line 5
From: person2@example.com
Line 7
   Subject: Two
Line 9
From: person3@example.com
   Subject: Three
The End
_

text.scan(/.*(From:.*?\n).*(Subject: Two)/m).join
  #=> "From: person2@example.com\nSubject: Two"

объяснение

Регулярное выражение

r = /.*(From:.*?\n).*(Subject: Two)/m

пропускает все символы, пока не достигнет последней строки "From:...\n" за которым следует (после нескольких несовпадающих символов) строка "Subject Two", В частности:

  • .*будучи жадным, потребляет столько символов, сколько может, включая строки"From:...\n" которые не соответствуют регулярному выражению, вплоть до начала первой группы захвата.
  • (From:.*?\n) первая группа захвата, захват "From: до конца этой линии. ? в .*? марки .* не жадный, так что останавливается на первом \n это достигает.
  • .* потребляет все последующие символы, пока не достигнет второй группы захвата.
  • (Subject: Two) это вторая группа захвата.

Убедитесь, что между первой и второй строкой есть только одна новая строка:

/(From[^\n]*\n\s*Subject:\s*Two)/m

посмотреть здесь

Кроме того, я считаю, что удаление /m сделает это еще проще:

/(From.*?\s*Subject:\s*Two)/

посмотреть здесь

Если у вас могут быть строки посередине, вам нужно использовать отрицательный прогноз:

/(From[^\n]*\n(^(?!From)[^\n]*\s*)*Subject:\s*Two)/m

посмотреть здесь

Это регулярное выражение делает следующее:

  1. From[^\n]*\n - соответствует тексту, начинающемуся с From до конца линии
  2. (^(?!From)[^\n]*\s*)* - соответствует нулю или нескольким строкам, не начинающимся с From ( отрицательный взгляд)
  3. Subject:\s*Two - соответствует тексту, содержащему Subject: [Пробел] Two

Использовать этот:

if subject =~ /^From[^\r\n]*\s*\S*Subject: Two/
    match = $&
else
    match = ""
end

объяснение

  • ^ якорь утверждает, что мы находимся в начале строки
  • From соответствует буквам
  • [^\r\n]* соответствует любым символам, которые не являются переносами строк
  • \s* соответствует любому пробелу, включая разрывы строк
  • \S* соответствует любым символам без пробелов
  • Subject: Two соответствует буквам

Многострочная версия

В ответ на ваш комментарий и новую заметку, вот еще одна версия, которая позволит несколько строк между From и Two:

if subject =~ /^From(?:(?:(?!^From).)*+\s*+)*\S*Subject: Two/
    match = $&
else
    match = ""
end
Другие вопросы по тегам