2
4

2 回答 2

3

假设您想要完全匹配(即tomato不匹配omat),那么您需要将每个包装在andp_i之间,然后加入它们。(?=^(?:)$)

如果你想要不精确的匹配(tomato确实匹配omat),那么你需要在andp_i之间包装每个,然后加入它们。请注意,在这种情况下,可能会发生灾难性的回溯。(?=.*?(?:))

.*在这两种情况下,如果你想吃这个词,你可以在加入之后添加(记住,前瞻匹配空字符串)。

解释

在确切的情况下,外部被包裹在一个前瞻中,因此没有字符被吃掉。内部是锚点^,并且$(这提供了准确性)围绕非捕获组。非捕获组是这样,如果您在其中一个的上层有 or 表达式p_i,则锚点将应用于整个组而不是第一个表达式。

不精确的情况是完全一样的,除了锚定,我们吃字符直到我们到达匹配位置。

您可以在www.debuggex.com上查看详细示例。

于 2013-04-19T03:13:40.223 回答
1
private static String combineRE(String p1, String p2){
    int groups1 = 0, groups2=0;
    StringBuilder newP = new StringBuilder("(?=");
    newP.append(p1);
    newP.append("$)(?=");

    Pattern capturingGroup = Pattern.compile("(?<!\\\\)(\\\\\\\\)*\\((?!\\?)");
    Matcher m = capturingGroup.matcher(p1);
    while(m.find()) groups1 ++;

    m = capturingGroup.matcher(p2);

    while(m.find()) groups2 ++;
    String new2 = p2;

    for(int i=1; i<=groups2; i++)
        new2 = new2.replaceAll("(?<!\\\\)\\\\"+i, "\\\\" + (i+groups1));

    newP.append(new2);
    newP.append("$).*");
    return newP.toString();
}

此函数使用基本结构(?=p1$)(?=p2$).*,同时在第二个模式中重新计算编号的反向引用。它使用正则表达式来计算每个模式中捕获组开启者的数量(未转义(的 s 后不跟 a ?),然后在将第二个模式放入结果模式之前更新第二个模式中的反向引用。我已经用 ideone 搭建了一个测试环境:请添加所有你能想到的测试用例,但我认为这回答了你的问题。

http://ideone.com/Wm8cRc

第 2 轮:

没有好的方法可以生成一个将find()子字符串匹配两个模式的模式。(?=p1(?<p2)).*(?<(?=p1)p2)在放弃并编写算法之前,我短暂地玩弄了其他此类废话。首先,我稍微修改了之前的 CombineRE:

private static String combineRE(String p1, String p2, boolean anchors){
    int groups1 = 0, groups2=0;
    StringBuilder newP = new StringBuilder((anchors)?"^(?=":"(?=");
    newP.append(p1);
    if (anchors) newP.append('$');
    newP.append(")(?=");

    Pattern capturingGroup = Pattern.compile("(?<!\\\\)(\\\\\\\\)*\\((?!\\?)");
    Matcher m = capturingGroup.matcher(p1);
    while(m.find()) groups1 ++;

    m = capturingGroup.matcher(p2);

    while(m.find()) groups2 ++;
    String new2 = p2;

    for(int i=1; i<=groups2; i++)
        new2 = new2.replaceAll("(?<!\\\\)\\\\"+i, "\\\\" + (i+groups1));

    newP.append(new2);
    if (anchors) newP.append('$');
    newP.append(')');
    if (anchors) newP.append(".*");
    return newP.toString();
}

您会看到它现在支持可选锚点。我在我的新功能中使用了这个功能:

private static String[] findAllCombinedRE(String p1, String p2, String haystack, boolean overlap){
    ArrayList<String> toReturn = new ArrayList<String>();
    Pattern pCombo = Pattern.compile(combineRE(p1,p2, false));
    String pComboMatch = combineRE(p1,p2, true);
    Matcher m = pCombo.matcher(haystack);
    int s = 0;
    while (m.find(s)){
        String match = haystack.substring(m.start());
        s = m.start()+1;
        for (int i=match.length(); i>0; i--){
            String sMatch = match.substring(0,i);
            if (Pattern.matches(pComboMatch, sMatch)){
                toReturn.add(sMatch);
                /**
                 *  Note that at this point we can caluclute match
                 *  object like Information:
                 *  
                 *  group() = sMatch;
                 *  start() = m.start();
                 *  end() = m.start() + i;
                 *  
                 *  If it so suited us, we could pass this information
                 *  back in a wrapped object.
                 */
                if (!overlap){
                    s = m.start()+i;
                    break;
                }
            }
        }
    }
    return toReturn.toArray(new String[]{});
}

它使用两个正则表达式的无锚版本来查找所有可能匹配的字符串,然后一次删除一个字母,直到字符串与锚版本匹配。它还包括一个布尔值来控制重叠匹配。

http://ideone.com/CBoBN5 效果很好。

于 2013-04-19T18:24:51.753 回答