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?
3 Réponses :
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:(.+?);)?
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
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