2
votes

Regex pour correspondre à 1 ou 2 occurrences

J'ai du texte avec la structure suivante:

nom_livre: SoftwareEngineering; auteur: John; auteur: Smith; nom_livre: DesignPatterns; auteur: Foo; auteur: Bar;

Le séparateur d'élément est;

Deux éléments author pourraient suivre l'élément book_name

Là peut être de 2 à 10 livres

Un livre doit avoir au moins un auteur, mais au maximum 2 auteurs

Je voudrais extraire book_name et des auteurs individuels pour chaque livre.

J'ai essayé l'expression régulière avec la méthode .scan (qui recueille toutes les correspondances):

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;"]]

Mais il ne recueille pas correctement les auteurs. Il ne recueille que le deuxième auteur du livre. Quelqu'un peut-il aider avec le problème?


0 commentaires

3 Réponses :


1
votes

Dans de nombreux moteurs, y compris Elixir, vous ne pouvez pas répéter plusieurs groupes de capture comme celui-ci et obtenir le résultat pour chaque groupe répété - vous n'obtiendrez que le dernier résultat d'un groupe de capture répété donné. Écrivez plutôt chaque groupe possible individuellement, puis filtrez les correspondances vides:

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

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


0 commentaires

1
votes

Vous n'avez pas besoin de regex pour cela, vous pouvez utiliser String. split / 3 :

|> Enum.map(fn
  [title, _, author1] -> {title, author1, nil}
  [title, _, author1, _, author2] -> {title, author1, author2}
end)

Résultat:

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

Pour simplifier, j'ai supposé qu'il y avait toujours deux auteurs. Le dernier Enum peut être remplacé par celui-ci, qui gère le cas où il n'y a pas de deuxième auteur aussi:

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


0 commentaires

1
votes

Cette partie (author:. + ?;) {1,2} du motif se répète 1 à 2 fois author , y compris ce qui suit jusqu'au point-virgule mais en répétant le groupe de capture comme celui-ci ne vous donnera que le dernier groupe de capture. Cette page pourrait être utile.

Au lieu d'utiliser un quantificateur non gourmand . *? vous ne pourriez pas faire correspondre un point-virgule répétant une classe de caractères inversée [^;] + qui ne correspond pas au point-virgule.

Vous pouvez également utiliser un groupe de capture et une backreference pour author . Le nom du livre est dans le groupe de capture 1, le nom du premier auteur du groupe 3 et du deuxième auteur facultatif dans le groupe 4.

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

Cela correspondra p >

  • book_name: correspond littéralement
  • ([^;] +); Le groupe 1 ne correspond pas à ; puis correspond à ;
  • (auteur): Groupe 2 author
  • ([^;] +); Le groupe 3 ne correspond pas à ; puis correspond à ;
  • (?: Groupe sans capture
    • \ 2: référence arrière à ce qui est capturé dans le groupe 2
    • ([^;] +); Le groupe 4 ne correspond pas à ; puis correspond à ;
  • )? Fermer le groupe non capturant et le rendre facultatif

démo regex101


0 commentaires