正则表达式匹配1或2次出现

问题描述 投票:1回答:3

我有以下结构的文字:

BOOK_NAME:软件工程;作者:John;作者:史密斯; BOOK_NAME:DesignPatterns;作者:富;作者:酒吧;

元素分隔符是;

两个作者元素可以遵循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;"]]

但它没有正确地收集作者。它只收集该书的第二作者。任何人都可以帮助解决这个问题吗?

regex elixir pcre
3个回答
1
投票

在许多引擎中,包括Elixir,您不能重复这样的多个捕获组并获得每个重复组的结果 - 您将只获得任何给定重复捕获组的最后结果。而是单独写出每个可能的组,然后过滤掉空匹配:

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

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


1
投票

你不需要正则表达式,你可以使用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)

1
投票

这部分(author:.+?;){1,2}模式重复1-2次author包括跟随分号但是重复捕获组这样只会给你最后一个捕获组。 This page可能会有所帮助。

而不是使用非贪婪的量词.*?,你可以匹配不是分号重复一个与分号不匹配的否定字符类[^;]+

您也可以使用author的捕获组和反向引用。该书的名称在于捕获组1,组3中第一作者的名称和组4中的可选第二作者。

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

这将匹配

  • book_name:字面意思匹配
  • ([^;]+);第1组匹配而不是;然后匹配;
  • (author): Group 2 author
  • ([^;]+); Group 3匹配而不是;然后匹配;
  • (?:非捕获组 \2:反对第2组中捕获的内容 ([^;]+); Group 4匹配而不是;然后匹配;
  • )?关闭非捕获组并使其成为可选项

regex101 demo

© www.soinside.com 2019 - 2024. All rights reserved.