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

Duk*_*ing 10 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
Run Code Online (Sandbox Code Playgroud)

代码在2次通过中执行matches:

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;
}
Run Code Online (Sandbox Code Playgroud)

nha*_*tdh 7

1.定义问题

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

鉴于任何模式K:

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

1我不知道是否有获得与K匹配的最长前缀.当然,你总是可以逐个删除最后一个字符并进行测试,K*直到匹配为止,但这显然效率低下.

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

2.在.NET中重复捕获组

如果我们有解决问题的方法,可以解决上面的问题:

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

  • 在字符串具有模式的情况下K*,通过匹配^(K)*$,我们可以获得与模式匹配的所有标记K.
  • 在字符串只有匹配的前缀的情况下K*,通过匹配^(K)*,我们可以获得匹配模式的所有标记K.

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

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

3. Java解决方案

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

如果K*相当于K*+

如果模式K具有以下属性:

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

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

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

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

如果K*不等于K*+

如果没有上述属性,我们需要2次通过才能解决问题.首先调用Matcher.matches()/ String.matches()打开模式K*.第二遍:

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

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

请注意,此解决方案普遍适用于任何K.换句话说,它也适用于K*等效的情况K*+(但我们将使用更好的一次通过解决方案).


Sco*_*ott 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());
        }       

    }
}
Run Code Online (Sandbox Code Playgroud)

正则表达式解释说:

<\\d>- 这是你上面定义的k模式
?=- 正向前瞻(检查K前面的内容)
<\\d>*- 匹配k 0次或更多次
$- 行尾
?<=- 正面看后面(检查K背后是什么)
^- 开始line
<\\d>*- 后跟0或更多Ks

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

编辑:正如@nhahtdh向我指出的那样,这只是答案的实现版本.实际上,可以通过答案中的知识来改进上述实现.
(<\\d>)(?=(<\\d>)*$)(?<=^(<\\d>)*)可以改为\\G<\\d>(?=(<\\d>)*$).