java解析数据文件格式

时间:2011-01-18 05:25:52

标签: java parsing tree

我有一个数据文件,如下所示:


0.5, 0.0 [
 1.5, -1.0 [
  inputs
 ]
 ;
 0.5, 1.0 [
  inputs
 ]
]

我正在尝试解析为树状结构。在上面的例子中,树应该像这样结束:


             Node (0.5, 0.0)
             /             \
   Node (1.5, -1.0)   Node (0.5, 1.0)
                 \     /
               Inputs Node

树结构就像任何基本树一样(除了所有最底层节点连接到相同的输入节点。

到目前为止,为了解析它,我有以下内容:


private void createSubLayer (String net, Node parent, int level) {
  level++;
  String[] nodes = net.split(";");

  for (String node : nodes) {
   if (node.equals("inputs")) {
    System.out.println("Connecting input @ " + level);
    for (Node n : inputs) {
     parent.connect(n);
    }
   }
   else {
    Node newNode;
    String[] nodeInfo = node.split("\\[", 2);
    String nodeDetails = nodeInfo[0];
    System.out.println(nodeInfo.length);
    System.out.println(nodeDetails);
    String subNet = nodeInfo[1].substring(0, nodeInfo[1].length() - 1);
    String[] nodeTW = nodeDetails.split(",");
    double threshhold = Double.parseDouble(nodeTW[0]);
    double weight = (nodeTW.length == 2) ? Double.parseDouble(nodeTW[1]) : 0.0;
    newNode = new Node(threshhold);
    newNode.setWeight(weight);
    System.out.println("Connecting new node @ " + level + "\n\tThreshhold: " + threshhold + "\n\tWeight: " + weight);
    if (parent != null) {
     parent.connect(newNode);
    }
    else {
     root = newNode;
    }

    System.out.println("Using subnet: " + subNet);
    createSubLayer(subNet, newNode, level);
   }
  }
 }

我用

来称呼它

createSubLayer(data_file_contents, null, 0);

到目前为止,这适用于非常基本的数据,例如


1.9, 1.0[inputs]

然而,当我在第一个例子中用分号分裂时,问题似乎就出现了。出于显而易见的原因,首先将其拆分(使用第一个示例):


0.5, 0.0 [
    1.5, -1.0 [
        inputs
    ]


    0.5, 1.0 [
        inputs
    ]
]

这不是预期的结果。

如何修改此解析过程(或者如果需要,修改数据文件结构)以创建所需的结果? (不要担心Node.connect()调用或其他任何东西,只要我能得到正确的结构)

作为一个相当简单的比较,这个结构基本上像一个XML文档,或JSON,或其他类似的格式,只缺少属性和节点名称(因为按顺序始终只有两个数字属性,节点内容)

感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

如果有递归结构,则需要进行正确的解析。我建议你研究http://www.antlr.org/以便轻松编写这样的解析器。

另一种方法是尝试编写一个手动递归下降解析器(你的格式告诉我它可以完成)但如果你不知道它是如何完成的,它可能看起来有点复杂。

一般的想法是有一个名为parseNode的方法,例如谁会查看下一个输入是number还是名称inputs。如果是数字将解析数字,直到找到[字符。之后,它会递归调用parseNode。在解析节点返回后,它将查看下一个char,如果它是],则表示它解析了所有子节点。如果没有,那么char应该是;并且它需要吃它并再次呼叫parseNode。一旦找到],它就会返回。

基本上我就是这样做的。

以下代码将正确解析您的字符串但请记住它绝对没有错误检查无效输入,没有正确解析字符串到数字等等。但它显示了我上面建议的一些工作代码。你不应该把这个代码放在生产中:)。

import java.util.ArrayList;
import java.util.List;

public class Main {

    static Node input = new Node();

    public static class Node {
        String numbers;
        List<Node> childs;
    }

    static class Input {
        String data;
        int pos;

        Input(String data, int pos) {
            this.data = data;
            this.pos = pos;
        }
    }

    public static void main(String[] args) {
        String data = "0.5, 0.0 [\n" +
                " 1.5, -1.0 [\n" +
                "  inputs\n" +
                " ]\n" +
                " ;\n" +
                " 0.5, 1.0 [\n" +
                "  inputs\n" +
                " ]\n" +
                "]";

        Node node = parseNode(new Input(data, 0));
    }

    private static Node parseNode(Input input) {
        StringBuffer stringBuffer = new StringBuffer();

        // eat chars until '[' or ']' or ';' or end of string
        boolean completed = false;
        char ch = input.data.charAt(input.pos);

        while (!completed && input.pos < input.data.length()) {
            ch = input.data.charAt(input.pos);
            switch (ch) {
                case '[':
                case ']':
                case ';':
                    completed = true;
                    break;
                default:
                    input.pos++;
                    stringBuffer.append(ch);
            }
        }

        String numbers = stringBuffer.toString().trim();

        if ( numbers.equalsIgnoreCase("inputs") ) {
            return Main.input;
        }

        Node thisNode = new Node();

        thisNode.numbers = numbers;
        thisNode.childs = new ArrayList<Node>();

        if ( ch == '[' ) { // we have childs
            do {
                input.pos++;
                thisNode.childs.add(parseNode(input));

                ch = input.data.charAt(input.pos);
                while ( ch != ';' && ch != ']' ) {
                    input.pos++;
                    ch = input.data.charAt(input.pos);
                }
            } while (ch == ';');

            if ( ch == ']' ) {
                input.pos++;
            }
        }

        return thisNode;
    }
}

答案 1 :(得分:2)

如果您的格式可能会发生变化(并且可能会更复杂一些),您可以考虑使用ANTLR之类的工具作为Toa​​der建议。然后你只需要编写(或改变)你的语法来生成一个(新的)词法分析器&amp;解析器。采用以下语法:

grammar Test;

parse
  :  element+ EOF
  ;

element
  :  numberList Open atom (SemiCol atom)* Close
  ;

numberList
  :  Decimal (Comma Decimal)*
  ;

atom
  :  element
  |  Identifier
  ;

Open       : '[';
Close      : ']';
Comma      : ',';
SemiCol    : ';';
Identifier : ('a'..'z' | 'A'..'Z')+;
Decimal    : '0'..'9'+ '.' '0'..'9'+;
Spaces     : (' ' | '\t' | '\r' | '\n') {skip();};

解释你的例子时:

0.5, 0.0 [
 1.5, -1.0 [
  inputs
 ]
 ;
 0.5, 1.0 [
  inputs
 ]
]

ANTLRWorks生成以下解析树:

alt text