提取所有出现的模式K并检查字符串是否与1遍中的“K *”匹配

时间:2013-05-16 11:51:45

标签: java regex

对于给定的输入字符串和给定的模式K,我想从字符串中提取每个出现的K(或其某些部分(使用组)),检查整个字符串是否匹配K*(因为它包含0个或更多K&没有其他字符)。

但我想使用正则表达式一次一次。更具体地说,我目前正在使用Matcher.find找到该模式,但这并非严格要求。

我该怎么做?

我已经找到了一个解决方案(并发布了一个答案),但想知道是否有特定的正则表达式或Matcher功能解决/可以解决这个问题,或者只是有更好/不同的方式正在做。但是,即使不是,我仍然认为这是一个有趣的问题。

示例:

模式:<[0-9]><>中的单个数字)

有效输入:<1><2><3>

输入无效:

<1><2>a<3>
<1><2>3
Oh look, a flying monkey!
<1><2><3

使用matches进行2次传递的代码:

boolean products(String products)
{
    String regex = "(<[0-9]>)";
    Pattern pAll = Pattern.compile(regex + "*");

    if (!pAll.matcher(products).matches())
        return false;

    Pattern p = Pattern.compile(regex);
    Matcher matcher = p.matcher(products);

    while (matcher.find())
        System.out.println(matcher.group());

    return true;
}

4 个答案:

答案 0 :(得分:7)

1。定义问题

由于当整个字符串与模式K*不匹配时不清楚要输出什么,我将重新定义问题以明确在这种情况下输出什么。

给出任何模式K:

  • 检查字符串是否具有模式K*
  • 如果字符串具有模式K*,则将字符串拆分为与K匹配的非重叠标记。
  • 如果字符串只有匹配模式K*的前缀,则选择由K*+ 1 选择的前缀,并将前缀拆分为与K匹配的标记。

1 我不知道是否有获得与K匹配的最长前缀。当然,您可以随时删除最后一个字符并对{{{ 1}}直到匹配,但显然效率低下。

除非另有说明,否则我在下面写的内容将遵循上面的问题描述。请注意,问题的第3个要点是解决要采用哪个前缀字符串的歧义。

2。在.NET中重复捕获组

如果我们能够解决问题,可以解决上述问题:

  

给定一个模式K*,这是一个重复捕获组,获取所有重复的捕获文本,而不是仅重复最后一次。

  • 如果字符串具有模式(K)*,则通过与K*匹配,我们可以获得与模式^(K)*$匹配的所有标记。
  • 如果字符串只有匹配K的前缀,则通过与K*匹配,我们可以获得与模式^(K)*匹配的所有令牌。

.NET正则表达式就是这种情况,因为它保留了重复捕获组的所有捕获文本。

但是,由于我们使用的是Java,因此我们无法访问此类功能。

3。 Java中的解决方案

检查字符串是否具有模式K始终可以使用K* / Matcher.matches()来完成,因为引擎会对输入字符串进行全面的回溯以某种方式“统一”带有输入字符串的String.matches()。困难的是将输入字符串拆分为与模式K*匹配的标记。

如果K等同于K*

如果模式K具有属性:

  

对于所有字符串 2 K*+等同于K*,即输入字符串如何拆分为与模式K*+匹配的标记是相同的

2 您可以仅为您正在操作的输入字符串定义此条件,但确保此前提条件并不容易。为所有字符串定义时,只需要分析正则表达式以检查条件是否成立。

然后可以构建解决问题的一次通过解决方案。您可以在模式K上重复使用Matcher.find(),并检查找到的最后一个匹配是否在字符串末尾。这与您当前的解决方案类似,不同之处在于您使用代码进行边界检查。

\GK中量词+之后的*使量词占有。占有量词将阻止引擎回溯,这意味着每次重复始终是模式K的第一个可能匹配。我们需要此属性以使解K*+具有相同的含义,因为它也将返回第一个可能的匹配对于模式K。

如果\GK不等同于K*

如果没有上面的属性,我们需要2次传递才能解决问题。首先在模式K*+上调用Matcher.matches() / String.matches()。第二遍:

  • 如果字符串与模式K*不匹配,我们将在模式K*上重复使用Matcher.find(),直到找不到更多匹配项。这可以通过我们如何定义当输入字符串与模式\GK不匹配时要采用的前缀字符串来完成。

  • 如果字符串与模式K*匹配,则在模式K*上重复使用Matcher.find()是一种解决方案。但是,这将导致与输入字符串的其余部分匹配的冗余工作。

请注意,此解决方案普遍适用于任何K.换句话说,它也适用于\GK(?=K*$)等同于K*的情况(但我们将使用更好的一次通过解决方案相反的情况。)

答案 1 :(得分:6)

这是对已经接受的答案的补充答案。下面是一个示例代码段,只使用m.find()一次通过模式,这与您的一次通过解决方案类似,但不会解析不匹配的行。

import java.util.regex.*;

class test{
    public static void main(String args[]){
        String t = "<1><2><3>";
        Pattern pat = Pattern.compile("(<\\d>)(?=(<\\d>)*$)(?<=^(<\\d>)*)");
        Matcher m = pat.matcher(t);
        while (m.find()) {
            System.out.println("Matches!");
            System.out.println(m.group());
        }       

    }
}

正则表达式解释说:

<\\d> - 这是你上面定义的k模式 ?= - 积极前瞻(检查K之前的内容)
<\\d>* - 匹配k 0次或更多次 $ - 行结束
?<= - 积极向后看(检查K背后的内容)
^ - 行的开头
<\\d>* - 后跟0或更多Ks

正则表达式是美好的事物。

编辑:正如@nhahtdh所指出的那样,这只是答案的实现版本。事实上,上述实施可以通过答案中的知识得到改善。
(<\\d>)(?=(<\\d>)*$)(?<=^(<\\d>)*)可以更改为\\G<\\d>(?=(<\\d>)*$)

答案 2 :(得分:3)

以下是使用Matcher.startMatcher.end的一次性解决方案。

boolean products(String products)
{
    String regex = "<[0-9]>";

    Pattern p = Pattern.compile(regex);

    Matcher matcher = p.matcher(products);
    int lastEnd = 0;
    while (matcher.find())
    {
        if (lastEnd != matcher.start())
           return false;
        System.out.println(matcher.group());
        lastEnd = matcher.end();
    }
    if (lastEnd != products.length())
        return false;
    return true;
}

唯一的缺点是它会在找到无效数据之前打印(或处理)所有值。

例如,products("<1><2>a<3>");将打印出来:

<1>
<2>
抛出异常之前的

(因为直到字符串有效为止)。

要么发生这种情况,要么暂时存放所有这些似乎都是不可避免的。

答案 3 :(得分:1)

    String t = "<1><2><3>";
    Pattern pat = Pattern.compile("(<\\d>)*");
    Matcher m = pat.matcher(t);
    if (m.matches()) {
        //String[] tt = t.split("(?<=>)"); // Look behind on '>'
        String[] tt = t.split("(?<=(<\\d>))"); // Look behind on K
    }