Регулярное выражение для соответствия 1 или 2 вхождения - PullRequest
0 голосов
/ 04 марта 2019

У меня есть текст со следующей структурой:

book_name: SoftwareEngineering; автор: John; автор: Smith;book_name: DesignPatterns; автор: Foo; автор: Bar;

Разделитель элементов:;

Два элемента автора могут следовать за элементом book_name

Может быть от 2 до 10 книг

В одной книге должен быть хотя бы один автор, но не более 2 авторов

Я хотел бы извлечь book_name и отдельных авторов для каждой книги.

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

iex> regex = ~r/book_name:(.+?;)(author:.+?;){1,2}/
iex> text = "book_name:SoftwareEngineering;author:John;author:Smith;book_name:DesignPatterns;author:Foo;author:Bar;"

iex> Regex.scan(regex, text, capture: :all_but_first)
[["SoftwareEngineering;", "author:Smith;"], ["DesignPatterns;", "author:Bar;"]]

Но он не собирает авторовправильно.Собирает только второй автор книги.Кто-нибудь может помочь с проблемой?

Ответы [ 3 ]

0 голосов
/ 04 марта 2019

Для этого вам не нужно регулярное выражение, вы можете использовать String.split/3:

defmodule Book do
  def extract(text) do
    text
    |> String.split("book_name:", trim: true)
    |> Enum.map(&String.split(&1, [":", ";"], trim: true))
    |> Enum.map(fn [title, _, author1, _, author2] -> {title, author1, author2} end)
  end
end

Вывод:

iex> Book.extract(text)
[{"SoftwareEngineering", "John", "Smith"}, {"DesignPatterns", "Foo", "Bar"}]

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

|> Enum.map(fn
  [title, _, author1] -> {title, author1, nil}
  [title, _, author1, _, author2] -> {title, author1, author2}
end)
0 голосов
/ 04 марта 2019

Эта часть (author:.+?;){1,2} шаблона повторяется 1-2 раза author, включая то, что следует до точки с запятой, но повторение подобной группы захвата даст вам только последнюю группу захвата. Эта страница может быть полезна.

Вместо использования не жадного квантификатора .*? вы можете сопоставить не точку с запятой, повторяющую класс отрицанных символов [^;]+, который не соответствует точке с запятой.

Вы также можете использовать группу захвата и обратную ссылку для author.Название книги находится в группе захвата 1, имя первого автора в группе 3 и необязательный второй автор в группе 4.

book_name:([^;]+);(author):([^;]+);(?:\2:([^;]+);)?

Это будет соответствовать

  • book_name: Совпадение буквально
  • ([^;]+); Совпадение группы 1 не ;, затем совпадение ;
  • (author): Группа 2 author
  • ([^;]+); Группа3 совпадения не ;, затем совпадение ;
  • (?: Группа без захвата
    • \2: обратная ссылка на то, что записано в группе 2
    • ([^;]+); Группа4 соответствия не ;, затем совпадение ;
  • )? Закрыть группу без захвата и сделать ее необязательной

regex101 demo

0 голосов
/ 04 марта 2019

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

book_name:(.+?;)author:(.+?);(?:author:(.+?);)?

https://regex101.com/r/LPgzcG/1

...