【问题标题】:Regex to match 1 or 2 occurrences正则表达式匹配 1 或 2 次出现
【发布时间】:2019-03-04 17:26:15
【问题描述】:

我有以下结构的文本:

书名:软件工程;作者:约翰;作者:史密斯; 书名:DesignPatterns;作者:Foo;作者:Bar;

元素分隔符是;

book_name 元素后面可以有两个 author 元素

可能有 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


    【解决方案1】:

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

    代替使用非贪婪量词.*?,您可以不匹配重复不匹配分号的否定字符类[^;]+ 的分号。

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

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

    这将匹配

    • book_name: 字面匹配
    • ([^;]+); 第 1 组匹配不匹配 ; 然后匹配 ;
    • (author): 第 2 组 author
    • ([^;]+); 第 3 组匹配不匹配 ; 然后匹配 ;
    • (?:非捕获组
      • \2: 反向引用第 2 组中捕获的内容
      • ([^;]+); 第 4 组匹配不匹配 ; 然后匹配 ;
    • )?关闭非捕获组并使其可选

    regex101 demo

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          猜你喜欢
          • 2015-11-14
          • 1970-01-01
          • 1970-01-01
          • 2015-09-19
          • 2012-02-24
          • 2015-04-10
          • 2016-03-18
          • 2019-07-19
          相关资源
          最近更新 更多