【问题标题】:What Java regular expression do I need to match this text?我需要什么 Java 正则表达式来匹配这个文本?
【发布时间】:2009-08-14 14:17:33
【问题描述】:

我正在尝试使用 Java 中的正则表达式匹配以下内容 - 我有一些数据由两个字符“ZZ”分隔。每条记录都以“ZZ”开头并以“ZZ”结束——例如,我想匹配一个没有结尾“ZZ”的记录,我想匹配下面的尾随“ZZanychars”(注意:* 不包含在字符串 - 他们只是标记我想要匹配的位)。

ZZanycharsZZZZanycharsZZZZanychars

但我不希望以下内容匹配,因为记录已结束:

ZZanycharsZZZZanycharsZZZZanycharsZZ

编辑:澄清一下 - 这是我正在使用的 2 个测试用例:

// This should match and in one of the groups should be 'ZZthree'
String testString1 = "ZZoneZZZZtwoZZZZthree";

// This should not match
String testString2 = "ZZoneZZZZtwoZZZZthreeZZ";

编辑:添加第三个测试:

// This should match and in one of the groups should be 'threeZee'
String testString3 = "ZZoneZZZZtwoZZZZthreeZee";

【问题讨论】:

  • “Z”会出现在数据中吗?例如,ZZZanycharsZZZZanyZcharsZZZZanycharsZ 是否会分解为两个完整的令牌(ZZZanycharsZZZZanyZcharsZZ)和一个不完整的令牌(ZZanycharsZ)?
  • 是的,单个 Z 可以出现在数据中 - 我添加了另一个测试字符串。现在变得棘手!
  • 好的,ZZanycharsZZZZZanycharsZZ 呢?是ZZanycharsZZZZZanycharsZZ,还是ZZanycharsZZZZZanycharsZZ?您如何确定额外的“Z”是第一个标记的一部分还是第二个标记的一部分?
  • 尾随的 ZZ 是非贪婪的,因此第一个结尾的 ZZ 会终止该令牌。在您的示例中,额外的“Z”将是第二个标记的一部分。

标签: java regex parsing


【解决方案1】:

(在第三个示例发布后编辑)

试试:

(?!ZZZ)ZZ((?!ZZ).)++$

演示:

import java.util.regex.*;

public class Main {
    public static void main(String[] args) {
        String[] tests = {
            "ZZoneZZZZtwoZZZZthree",
            "ZZoneZZZZtwoZZZZthreeZZ",
            "ZZoneZZZZtwoZZZZthreeZee"
        };
        Pattern p = Pattern.compile("(?!ZZZ)ZZ((?!ZZ).)++$");
        for(String tst : tests) {
            Matcher m = p.matcher(tst);
            System.out.println(tst+" -> "+(m.find() ? m.group() : "no!"));
        }
    }
}

【讨论】:

  • 基于最新版本的规范,我认为你应该回到(?!ZZ) 版本。可以匹配三个 Z 开头,如果它们前面有两个 Z 或字符串的开头:(?<=[^Z]ZZ|^)
【解决方案2】:

只匹配最终的、未终止的记录:

(?<=[^Z]ZZ|^)ZZ(?:(?!ZZ).)++$

起始分隔符是两个Z,但可以有第三个Z,它被视为数据的一部分。向后查找可确保您不匹配作为前一个记录的结束分隔符的一部分的 Z(因为结束分隔符 不能 前面有非分隔符 Z)。但是,这假设永远不会有空记录(或仅包含单个 Z 的记录),这可能导致连续出现八个或更多 Z

ZZabcZZZZdefZZZZZZZZxyz

如果可能的话,我会忘记尝试自己匹配最终记录,而是从头开始匹配所有条记录:

(?:ZZ(?:(?!ZZ).)*+ZZ)*+(ZZ(?:(?!ZZ).)++$)

最终的未终止记录现在在第 1 组中捕获。

【讨论】:

  • 这个解决方案是我现在使用的解决方案。这是一些正则表达式的魔法!
  • “魔术”是正则表达式的好词:能够做出奇妙的事情,但喜怒无常,而且从未被完全理解。 :)
【解决方案3】:

我建议类似...

/ZZ(.*?)(ZZ|$)/

这将匹配:

  1. ZZ — 文字字符串
  2. (.*?) — 任何字符
  3. (ZZ|$) — 另一个 ZZ 文字,或者字符串的结尾

【讨论】:

  • 我认为他特别希望不匹配记录末尾的 ZZ 文字。
  • @Platinum Azure:我只想匹配一个结尾没有 ZZ 的尾随记录。
【解决方案4】:
^ZZ.*(?<!ZZ)$


Assert position at the beginning of the string «^»
Match the characters “ZZ” literally «ZZ»
Match any single character that is not a line break character «.*»
   Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
Assert that it is impossible to match the regex below with the match ending at this position (negative lookbehind) «(?<!ZZ)»
   Match the characters “ZZ” literally «ZZ»
Assert position at the end of the string (or before the line break at the end of the string, if any) «$»


Created with RegexBuddy

【讨论】:

  • +1 用于使用前瞻以避免将“Z”匹配为终端字符串。
【解决方案5】:

这有一个棘手的部分:ZZ 既是开始标记又是结束标记。

有一个开始情况(ZZ,后面没有另一个 ZZ,这表示第一个 ZZ 实际上是一个结束标记)和两个结束情况(ZZ 字符串结尾,ZZ 后跟 ZZ)。目标是匹配开始情况,而不是任何结束情况。

为此,我的建议如下:

/ZZ(?!ZZ)(.*?)(ZZ(?!(ZZ|$))|$)/

对于字符串ZZfooZZZZbarZZbazZZ

  • 这将不匹配合法记录 ZZfooZZ:ZZ,后面不跟 ZZ,后面跟任意字符组合(这里是“foo”),后面跟 ZZ,但是 ZZ 后面跟 ZZ,这会打开下一条记录.
  • 检查的下一部分是 foo 之后的 ZZ。这失败了,因为 ZZ 后面不能跟着另一个 ZZ,但在这种情况下它是。这是我们想要的,因为 foo 之后的 ZZ 无论如何都不会开始新记录。
  • 小节之前的 ZZ 后面没有另一个 ZZ,因此它是合法的记录开始。 "bar" 被 .*? 消耗。然后是一个 ZZ,但后面没有另一个 ZZ 或字符串结尾,这意味着 ZZbar 令牌不好。
    • (它可以被人类解释为 ZZbarZZ 而 bazZZ 无效,但在任何一种情况下都有问题,所以我只是编写了正则表达式来考虑此处出现格式错误的记录)
    • 因此 ZZbar 将被正则表达式捕获/匹配,视为非法。
  • 小节后的 ZZ 后面没有 ZZ,后面跟着 baz,后面跟着一个 ZZ,该 ZZ 未通过先行断言,说明它后面不能跟字符串结尾。所以 ZZbazZZ 是合法记录,不会在正则表达式中捕获。

还有一种情况:对于ZZfoo,开头的 ZZ 没问题,捕获了 foo,然后正则表达式指出它是字符串的结尾,并且没有发生 ZZ。因此,ZZfoo 被视为非法匹配。

如果这没有意义,请告诉我,以便我更清楚地说明。

【讨论】:

    【解决方案6】:

    尝试删除 ZZallcharsZZ 的所有匹配项怎么样,剩下的就是你想要的。

    ZZ.*?ZZ
    

    【讨论】:

      猜你喜欢
      • 2021-12-31
      • 2011-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-24
      • 2010-09-23
      • 1970-01-01
      相关资源
      最近更新 更多