【问题标题】:Match custom pattern in regex multiple times多次匹配正则表达式中的自定义模式
【发布时间】:2021-09-14 17:06:58
【问题描述】:

我正在尝试解析一个查询,我需要对其进行修改以将特定属性及其值替换为另一个属性和不同的值。我正在努力编写一个匹配我需要的指定属性及其值的正则表达式。

这里有一些例子来说明我的观点。 test:property 是我们需要匹配的属性名。

  1. 具有单个值的属性:test:property:schema:Person
  2. 具有多个值的属性(可以有多少个值没有限制 - 本示例使用 3 个):test:property:(schema:Person OR schema:Organization OR schema:Place)
  3. 括号中包含单个值的属性:test:property:(schema:Person)
  4. 查询字符串中具有另一个属性的属性(即字符串的其他部分我不感兴趣):test:property:schema:Person test:otherProperty:anotherValue

另请注意,其他组合也是可能的,例如其他属性位于我需要捕获的属性之前,我的属性具有多个值,而查询中存在另一个属性。

我想在整个test:property 部分上匹配在该匹配中捕获的每个值。鉴于上面的例子,这些是我正在寻找的结果:

# Match Groups
1 test:property:schema:Person schema:Person
2 test:property:(schema:Person OR schema:Organization OR schema:Place) schema:Person
schema:Organization
schema:Person
3 test:property:(schema:Person) schema:Person
4 test:property:schema:Person schema:Person

注意:#1 和#4 产生相同的输出。我想说明应该忽略字符串的其余部分(我只需要更改test:property 键和值)。

schema:Person 的模式定义为\w+\:\w+,即一个或多个单词字符,后跟一个冒号,然后是一个或多个单词字符。

如果我们用名称定义字符串的已知部分,我想我可以表达我想要匹配的内容。

  • schema:Person - <TypeName> - 请注意,在这种情况下,第一部分 schema 不是固定的,可以不同
  • test:property - <MatchProperty>
<MatchProperty>: // property name (which is known and the same - in the examples this is `test:property`) followed by a colon
  ( // optional open bracket
    <TypeName>
    (OR <TypeName>)* // optional additional TypeNames separated by an OR
  ) // optional close bracket

我发现的每个示例在重复部分都有简单的字母数字字符,但我的重复模式包含冒号,这似乎让我感到困惑。我得到的最接近的是:

(test\:property:(?:\(([\w+\:\w+]+ [OR [\w+\:\w+]+)\))|[\w+\:\w+]+)

当没有其他属性时,这可以正常工作(尽管例如 #2 的匹配包含整个属性和值作为第一组结果,第二组包含属性值)但是当包含其他属性时会变得疯狂。

另外,将正则表达式放入https://regex101.com/ 我知道这是不正确的,因为方括号中的反斜杠字符完全匹配。我开始尝试使用捕获组和非捕获组,但在放弃之前做到了这一点!

(?:(\w+\:\w+))(?:(\sOR\s))*(?:(\w+\:\w+))*

【问题讨论】:

  • 最多有 3 个“组”吗?
  • 所以您的&lt;MatchProperty&gt; 并不总是test:property?你&lt;TypeName&gt; 的第一部分并不总是schema?你能添加一个特定的语言吗?例如,.NET 可以捕获的不仅仅是捕获组中的最后一个事件。
  • 测试用例 #1 和 #4 相同!?
  • 组数在模式内设置。我建议在test:property 之后捕获整个部分,然后用空格+OR + 空格分割捕获的值。见\btest:property:(\()?(\w+:\w+(?:\s+OR\s+\w+:\w+)*)(?(1)\))regex demo
  • 我已更新问题以回答这些 cmets 中提出的所有问题,我希望这能增加一些清晰度。简而言之:有无限数量的组,&lt;MatchProperty&gt; 是一个已知值,&lt;TypeName&gt; 的第一部分并不总是schema,我已经阐明了示例 #1 和 #4 的原因

标签: java regex java-11


【解决方案1】:

如果您想要纯正则表达式,这不是一个完整的解决方案,因为正则表达式和 Java 正则表达式存在一些限制,但我想出的正则表达式似乎有效。

如果您要匹配整个序列,以下正则表达式将起作用。

test:property:(?:\((\w+:\w+)(?:\sOR\s(\w+:\w+))*\)|(\w+:\w+))

不幸的是,重复捕获组只会捕获最后一个匹配项,因此在具有多个值的查询(如示例 2)中,组 1 和 2 将是第一个和最后一个值(schema:Person 和 schema:Place)。在没有括号的查询中,该值将在第 3 组中。

如果您知道值的最大数量,您可以生成一个包含足够组的大量正则表达式,但这可能并不理想,具体取决于您的应用程序。

另一个在任意长度的组中查找值的正则表达式使用正则表达式的正向回溯来匹配有效值。然后可以生成an array of matches

(?<=test:property:(?:(?:\((?:\w+:\w+\sOR\s)+)|\(?))\w+:\w+

这个方法的问题是它看起来像Java lookbehind has some limitations,特别是不允许unbound or complex quantifiers。我不是 Java 人,所以我没有自己尝试过,但似乎这也行不通。如果其他人有其他解决方案,请发布另一个答案!

考虑到这一点,我可能会建议使用组合正则表达式 + 字符串解析方法。您可以使用正则表达式解析出一个或多个值(由 OR 分隔),然后拆分字符串以获得最终值。

要匹配括号内的整个部分或没有括号的单个值,您可以使用此正则表达式:

test:property:(?:\((\w+:\w+(?:\sOR\s\w+:\w+)*)\)|(\w+:\w+))

它仍然分为两组,一组匹配带括号的值,另一组匹配不带括号的值(以避免匹配不成对的括号),但它应该是可用的。

如果您想玩转这些正则表达式或了解更多信息,这里有一个正则表达式:https://regexr.com/65kma

【讨论】:

    猜你喜欢
    • 2020-10-10
    • 1970-01-01
    • 2022-10-04
    • 1970-01-01
    • 1970-01-01
    • 2017-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多