如何使用 REGEX 匹配另一个模式之前最后一次出现的模式

How do I match the last occurrence of Pattern before another Pattern with REGEX

我有一个巨大的 XML 文件,我需要提取包含一系列数字的整个标签的内容。 在我的文件中所有内容都是一行,我在此处添加了换行符以使其更具可读性

所以这里我有一个简化的例子

文件:

<ORDERS>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>12345</tag3><tag4>ccc</tag4></IDOC>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>23456</tag3><tag4>ccc</tag4></IDOC>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>0007537181</tag3><tag4>ccc</tag4></IDOC>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>34567</tag3><tag4>ccc</tag4></IDOC>
</ORDER>

我想匹配包含序列 0007537181 的 IDOC BEGIN 标签。所以它将是

<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>0007537181</tag3><tag4>ccc</tag4></IDOC>

到目前为止我得到了这个正则表达式:

cat myfile | grep -oP '<IDOC BEGIN.*?0007536846.*?</IDOC>'

结果是从第一个同名标签开始到我想要的标签:

<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>12345</tag3><tag4>ccc</tag4></IDOC>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>23456</tag3><tag4>ccc</tag4></IDOC>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>0007537181</tag3><tag4>ccc</tag4></IDOC>

我设法通过将其发送到第二个正则表达式来解决这个问题,该正则表达式获取最后一次出现的 IDOC BEGIN

cat myfile | grep -oP '<IDOC BEGIN.*?0007536846.*?</IDOC>' | grep -oP '<IDOC BEGIN(?!.*<IDOC BEGIN).*?</IDOC>'

总而言之,我需要获取数字序列之前的最后一个 IDOC BEGIN

请记住,原始文件没有换行符,所有内容都在一行中。

您可以使用的正则表达式是基于放在开头的贪心点模式,然后是 \K match reset operator, or based on a 。当涉及到具有部分匹配(但不匹配)的大字符串时,两者都非常不安全。

所以,两个正则表达式是

.*\K<IDOC BEGIN.*?0007536846.*?</IDOC>
<IDOC BEGIN(?:(?!<IDOC BEGIN).)*?0007536846(?:(?!<IDOC BEGIN).)*?</IDOC>

最好的办法是在这些情况下展开经过调整的贪婪令牌:

<IDOC BEGIN[^<]*(?:<(?!IDOC BEGIN)[^<]*?)*0007537181.*?</IDOC>

regex demo

第一个.*?替换为[^<]*(?:<(?!IDOC BEGIN)[^<]*?)*:

  • [^<]* - 否定字符 class 匹配除 < 之外的 0 个或更多字符,尽可能多
  • (?:<(?!IDOC BEGIN)[^<]*?)* - 0 次或多次重复
    • <(?!IDOC BEGIN) - < 字符后没有紧跟着 IDOC BEGIN 字符串
    • [^<]*? - 否定字符 class 匹配除 < 之外的 0 个或多个字符,尽可能少