0

我正在尝试使用正则表达式通过提取某些文本来解析文件。标准包不支持我需要使用的正则表达式java.util.regex(因为我需要匹配嵌套结构,例如嵌套{}括号和其他类似的东西),所以我决定尝试一下JRegex,它声称可以完全处理 Perl 5.6 正则表达式语法。但是,当我尝试将此包与递归正则表达式一起使用以匹配嵌套{}括号时遇到了问题:

Pattern p = new Pattern("(\\{(?:(?1)*|[^{}]*)+\\}|\\w+)");  // jregex.Pattern
线程“主”jregex.PatternSyntaxException 中的异常:“(?”之后的错误字符:1

然而,类似的正则表达式/(\{(?:(?1)*|[^{}]+)+\}|\w+)/sg在 Perl 中按预期工作。所以,我的下一个想法是找到一种方法来在Perl中解析文件,然后将结果传递给Java(最好以字符串数组或类似的形式),我的问题是:最好的方法是什么在这种情况下?或者,我忽略了另一种更简单的选择吗?

4

3 回答 3

3

JRegex 似乎不支持递归匹配,所以我建议您只使用java.util.regex并设置嵌套级别数的限制。

例如,要允许多达 50 层嵌套,每层都有“无限”数量的括号对(最深的除外),您可以使用

// Set the maximum number of nested levels required.
int max = 50;
String regex = "(?R)";

while (--max > 0) {
    regex = regex.replace("(?R)", "(?>\\{(?:[^{}]*+|(?R))+\\})");
}

// Ensure no (?R) in the final and deepest replacement.
regex = regex.replace("(?R)", "\\{[^{}]*+\\}") + "|\\w+";

String str = " {{}{}} {abc} {{de}{fg}} hij {1{2{3{4{5{6{7{8{9{10{11{12{13{14{15{16{17{18{19{20{21{22{23{24{25{26{27{28{29{30{31{32{33{34{35{36{37{38{39{40{41{42{43{44{45{46{47{48{49{50}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} {end}";
Matcher m = Pattern.compile(regex).matcher(str);

while (m.find()) {
    System.out.println(m.group());
}

/*
 {{}{}}
 {abc}
 {{de}{fg}}
 hij
 {1{2{3{4{5{6{7{8{9{10{11{12{13{14{15{16{17{18{19{20{21{22{23{24{25{26{27{28{29{30{31{32{33{34{35{36{37{38{39{40{41{42{43{44{45{46{47{48{49{50}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
 {end}
*/

以上构建了一个正则表达式,如果支持递归匹配,则可以使用它(?>\\{(?:[^{}]*+|(?R))+\\})并反复替换(?R)整个模式。

因为在创建的表达式中有很多嵌套的量词,原子分组(?>)和所有格量词+用于限制回溯并确保正则表达式在找不到匹配项时快速失败。尽管正则表达式可能很长,但它会很有效。

如果您不想或无法对嵌套设置限制,或者担心冗长的正则表达式的想法,您可以通过简单地遍历文件文本并跟踪打开和关闭括号的数量来解析嵌套括号, 例如

List<String> list = new ArrayList<String>();
int strLen = str.length();

for (int i = 0; i < strLen; i++) {
    char c = str.charAt(i);

    if (c == '{') {
        int b = 1;
        StringBuilder sb = new StringBuilder("{");

        while (b > 0 && i < strLen - 1) {
            sb.append( c = str.charAt(++i) );

            if (c == '}') b--;
            else if (c == '{') b++;
        }
        list.add(sb.toString());
    }
}

for (String s : list) { System.out.println(s); }

这似乎比与 Perl 交互的麻烦要少得多,但是请参阅诸如How should I call a Perl Script in Java?之类的答案。如果那是你想要做的。

于 2013-03-09T16:50:15.630 回答
1

最好的方法是标记输入并通过标记流将其发送到您的解析器,然后根据您的需要自上而下/自下而上地解析它。正则表达式并不总是有助于解析嵌套结构。


JLex 实用程序基于 Lex 词法分析器生成器模型。JLex 采用类似于 Lex 接受的规范文件,然后为相应的词法分析器创建 Java 源文件。

看看JLex,因为它可以帮助您用非常简单的代码为您的案例生成词法分析器。

于 2013-03-09T13:02:40.013 回答
0

正则表达式不能真正处理嵌套分隔符。我过去曾通过使用正则表达式来查找分隔符,然后使用简单的有限状态机来解析结果数组来解决这个问题。

于 2013-03-09T13:01:08.050 回答