使用正则表达式解析文本文件

时间:2013-03-09 12:57:29

标签: java regex perl

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

Pattern p = new Pattern("(\\{(?:(?1)*|[^{}]*)+\\}|\\w+)");  // jregex.Pattern
Exception in thread "main" jregex.PatternSyntaxException: wrong char after "(?": 1

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

3 个答案:

答案 0 :(得分: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?等答案。

答案 1 :(得分:1)

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


  

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

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

答案 2 :(得分:0)

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