用Java正则表达式递归替换?

时间:2012-03-16 08:33:55

标签: java regex recursion

我可以使用以下代码ABC(10,5)替换(10)%(5)

replaceAll("ABC\\(([^,]*)\\,([^,]*)\\)", "($1)%($2)")

但是我无法弄清楚如何为ABC(ABC(20,2),5)ABC(ABC(30,2),3+2)做到这一点。

如果我能够转换为((20)%(2))%5,我该如何转换回ABC(ABC(20,2),5)

谢谢, Ĵ

4 个答案:

答案 0 :(得分:1)

我将回答第一个问题。我无法在一个replaceAll中完成任务。我认为它甚至无法实现。但是,如果我使用循环,那么这应该为你工作:

    String termString = "([0-9+\\-*/()%]*)";
    String pattern = "ABC\\(" + termString + "\\," + termString + "\\)";
    String [] strings = {"ABC(10,5)", "ABC(ABC(20,2),5)", "ABC(ABC(30,2),3+2)"};
    for (String str : strings) {
        while (true) {
            String replaced = str.replaceAll(pattern, "($1)%($2)");
            if (replaced.equals(str)) {
                break;
            }
            str = replaced;
        }
        System.out.println(str);
    }

我假设您正在为数字表达式编写解析器,因此定义了术语termString = "([0-9+\\-*/()%]*)"。它输出:

(10)%(5)
((20)%(2))%(5)
((30)%(2))%(3+2)

编辑根据OP请求,我添加了解码字符串的代码。它比前进的场景更加苛刻:

    String [] encoded = {"(10)%(5)", "((20)%(2))%(5)", "((30)%(2))%(3+2)"};
    String decodeTerm = "([0-9+\\-*ABC\\[\\],]*)";
    String decodePattern = "\\(" + decodeTerm + "\\)%\\(" + decodeTerm + "\\)";
    for (String str : encoded) {
        while (true) {
            String replaced = str.replaceAll(decodePattern, "ABC[$1,$2]");
            if (replaced.equals(str)) {
                break;
            }
            str = replaced;
        }
        str = str.replaceAll("\\[", "(");
        str = str.replaceAll("\\]", ")");
        System.out.println(str);
    }

输出是:

ABC(10,5)
ABC(ABC(20,2),5)
ABC(ABC(30,2),3+2)

答案 1 :(得分:1)

您可以先开始评估最可重复的内部表达式,直到不再存在redux。但是,您必须照顾其他,()。 @BorisStrandjev的解决方案更好,更具防弹性。

String infix(String expr) {
    // Use place holders for '(' and ')' to use regex [^,()].
    expr = expr.replaceAll("(?!ABC)\\(", "<<");
    expr = expr.replaceAll("(?!ABC)\\)", ">>");
    for (;;) {
        String expr2 = expr.replaceAll("ABC\\(([^,()]*)\\,([^,()]*)\\)",
                "<<$1>>%<<$2>>");
        if (expr2 == expr)
            break;
        expr = expr2;
    }
    expr = expr.replaceAll("<<", ")");
    expr = expr.replaceAll(">>", ")");
    return expr;
}

答案 2 :(得分:1)

您可以使用此正则表达式库https://github.com/florianingerl/com.florianingerl.util.regex,它也支持递归正则表达式。

将ABC(ABC(20,2),5)转换为((20)%(2))%(5)如下所示:

    Pattern pattern = Pattern.compile("(?<abc>ABC\\((?<arg1>(?:(?'abc')|[^,])+)\\,(?<arg2>(?:(?'abc')|[^)])+)\\))");
    Matcher matcher = pattern.matcher("ABC(ABC(20,2),5)");
    String replacement = matcher.replaceAll(new DefaultCaptureReplacer() {
        @Override
        public String replace(CaptureTreeNode node) {
            if ("abc".equals(node.getGroupName())) {
                return "(" + replace(node.getChildren().get(0)) + ")%(" + replace(node.getChildren().get(1)) + ")";
            } else
                return super.replace(node);
        }

    });
    System.out.println(replacement);
    assertEquals("((20)%(2))%(5)", replacement);

再次转换,即从((20)%(2))%(5)转换回ABC(ABC(20,2),5),如下所示:

    Pattern pattern = Pattern.compile("(?<fraction>(?<arg>\\(((?:(?'fraction')|[^)])+)\\))%(?'arg'))");
    Matcher matcher = pattern.matcher("((20)%(2))%(5)");
    String replacement = matcher.replaceAll(new DefaultCaptureReplacer() {
        @Override
        public String replace(CaptureTreeNode node) {
            if ("fraction".equals(node.getGroupName())) {
                return "ABC(" + replace(node.getChildren().get(0)) + "," + replace(node.getChildren().get(1)) + ")";
            } else if ("arg".equals(node.getGroupName())) {
                return replace(node.getChildren().get(0));
            } else
                return super.replace(node);
        }

    });
    System.out.println(replacement);
    assertEquals("ABC(ABC(20,2),5)", replacement);

答案 3 :(得分:0)

您可以尝试使用波兰表示法重写字符串,然后将%X Y 替换为 ABC(X,Y)

Here是波兰表示法的维基链接。

问题是当你在字符串中递归替换它们时,你需要找出首先发生了哪些 ABC(X,Y)的重写。波兰表示法对于解密&#34;这些重写的顺序发生并广泛用于表达式评估。

您可以通过使用堆栈并记录首先发生的替换来执行此操作:找到最内部的括号集,仅将该表达式推送到堆栈中,然后从字符串中删除它。当您想要重建表达式原始表达式时,只需从堆栈顶部开始并应用反向转换(X)%(Y) - &gt;的 ABC(X,Y)

这有点是波兰表示法的一种形式,唯一的区别是你不将整个表达式存储为字符串,而是将其存储在堆栈中以便于处理。

简而言之,在替换时,从最内层的术语(其中没有括号的术语)开始并应用反向替换。

使用(X)%(Y) - &gt;可能会有所帮助 ABC {X,Y} 作为中间重写规则,然后将大括号重写为圆括号。这样就可以更容易地确定哪个是最内部的术语,因为新术语不会使用圆括号。它也更容易实现,但不是那么优雅。