Несколько повторяющихся захватов регулярного выражения Ruby

Я пытаюсь проанализировать подмножество веб-страницы с регулярным выражением просто для удовольствия. Было весело, пока я не столкнулся со следующей проблемой. У меня есть параграф, как показано ниже;

foo: 1, 2, 3, 4 and 5.
bar: 1, 2 and 3.

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

foo:(?:\s(\d)(?:,|\sand|\.))+

Это соответствует приведенной выше строке, но захватывает только последнее вхождение группы захвата, которой является 5.

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

Вы не можете в Ruby без помощи \Grubular.com/r/VKOaLEYmSI

revo 11.03.2018 00:44

Да, но не нужно использовать \K, подойдет s.scan(/(?:foo:\s*|(?!\A)\G\s*(?:,|and)?\s*)(\d+)/). Зато s[/foo:([^.]+)/,1].scan(/\d+/) красивее выглядит.

Wiktor Stribiżew 11.03.2018 01:01

Хотя это не повлияло на Rubular способ отображения результатов, \K предназначен для чистого представления чисел.

revo 11.03.2018 01:12

Этот \G сделал фокус, спасибо, но не могли бы вы опубликовать это в качестве ответа с объяснением \G и, возможно, \K? Я уже провел поиск и нашел, что он делает, но пояснительный ответ может помочь тому, кто ищет ответ для аналогичной проблемы.

Foo Bar Zoo 11.03.2018 04:52

Сделайте себе хеш: str.scan(/\w+:.*?\./).map { |s| [s[/\w+/].to_sym,s.scan(/\d+/)]}.to_h #=> {:foo=>["1", "2", "3", "4", "5"], :bar=>["1", "2", "3"] }

Sagar Pandya 11.03.2018 06:40

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

Foo Bar Zoo 11.03.2018 07:29

Кто является владельцем «Твоего решения»? Пожалуйста, начните свои комментарии с имени пользователя человека, которому вы адресуете свой комментарий, которому предшествует "@". Посмотрите на другие вопросы SO, и вы поймете, что я имею в виду.

Cary Swoveland 11.03.2018 09:29

@CarySwoveland Думаю, в моих комментариях нет двусмысленности. Если вы прочитаете все комментарии в хронологическом порядке, их цели довольно ясны. Но буду иметь это в виду, спасибо за совет.

Foo Bar Zoo 11.03.2018 12:56

@revo ваше регулярное выражение решило мою проблему, спасибо.

Foo Bar Zoo 11.03.2018 12:57

Отлично, @revo! Опубликуйте свое решение в качестве ответа, желательно с регулярным выражением, представленным в режиме свободного интервала, с документированием каждого элемента (как я сделал в своем ответе). Вы уверены, что \K [необходим] (rubular.com/r/zfNjHXL9Rl)? Документ для Строка # сканирование говорит о необходимости \K: «Если шаблон содержит группы, каждый отдельный результат сам по себе является массивом, содержащим по одной записи для каждой группы». (т.е. этот \K не нужен). Симулятор регулярных выражений предполагает, что на самом деле это свойство MatchData. Нет?

Cary Swoveland 11.03.2018 18:10

FooBarZoo, \K можно прочитать как «забыть все, что было найдено до сих пор». Строка перед \K должна совпадать, но не является частью возвращаемого совпадения. Здесь все совпадения, но значения групп захвата не меняются.

Cary Swoveland 11.03.2018 18:11

@CarySwoveland Спасибо, я добавил ответ. Ранее я сказал, что использую \K только для чистого представления цифр в демонстрации (предыдущие комментарии). Однако я не заметил, что вывод для совпадающих частей отличается от вывода групп захвата (в regex101 это другое) с самого начала.

revo 11.03.2018 21:24
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
2
12
1 227
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

str = "foo: 1, 2, 34, 4 and 5. and 6."

r = /
    \d+             # match one or more digits
    (?=[^.]+:oof\z) # match one or more digits other than a period, followed
                    # by ":oof" at the end of the string, in a positive lookahead
    /x              # free-spacing regex definition mode

str.reverse.scan(r).join(' ').reverse.split
  #=> ["1", "2", "34", "4", "5"]

Шаги следующие.

s = str.reverse
  #=> ".6 dna .5 dna 4 ,43 ,2 ,1 :oof"
a  = s.scan r
  #=> ["5", "4", "43", "2", "1"]
b  = a.join(' ')
  #=> "5 4 43 2 1"
c  = b.reverse
  #=> "1 2 34 4 5"
c.split
  #=> ["1", "2", "34", "4", "5"]

Если совпадений нет, возвращается пустой массив.

Итак, почему все задним ходом? Это позволяет мне использовать положительный смотреть вперед, который, в отличие от положительного смотреть за, разрешает совпадения переменной длины.

Ответ принят как подходящий

Данные повторяющейся группы захвата не хранятся отдельно на большинстве языков программирования, поэтому вы не можете ссылаться на них по отдельности. Это веская причина использовать якорь \G. \G заставляет совпадение начинаться с того места, где закончилось предыдущее совпадение, или оно будет соответствовать началу строки так же, как \A.

Итак, нам нужна его первая возможность:

(?:foo:|\G(?!\A))\s*(\d+)\s*(?:,|and)?

Авария:

  • (?: Запустить группу без захвата
    • foo: Соответствует foo:
    • | Или
    • \G(?!\A) Продолжить матч с того места, где закончился предыдущий
  • ) Конец NCG
  • \s* Любое количество пробельных символов
  • (\d+) Совпадение и захват цифр
  • \s* Любое количество символов whitespae
  • (?:,|and)? Дополнительный , или and

Это регулярное выражение начнет сопоставление при встрече foo во входной строке. Затем пытается найти следующую цифру, которая предшествует запятой или and (вокруг цифр разрешены пробелы).

Токен \K сбросит совпадение. Это означает, что он отправит сигнал движку, чтобы забыть все, что было найдено до сих пор (но сохранить все, что было захвачено), а затем оставит курсор прямо в этой позиции.

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

Другие вопросы по теме