формат файла анализа данных Java - PullRequest
1 голос
/ 18 января 2011

У меня есть файл данных, который выглядит следующим образом:

<code>
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 ]

3 голосов
/ 18 января 2011

Вам нужно выполнить правильный анализ, когда у вас есть рекурсивные структуры, как в этом случае. Я бы посоветовал вам заглянуть в http://www.antlr.org/, чтобы легко написать такой парсер.

Другой подход - попытаться написать ручной синтаксический анализатор с рекурсивным спуском (ваш формат подсказывает мне, что это можно сделать), но это может показаться немного сложным, если у вас нет идеи, как это сделать.

Общая идея состоит в том, чтобы, например, иметь метод с именем parseNode, который бы смотрел, является ли следующий вход либо number, либо именем, например inputs. Если это число, оно будет анализировать числа до тех пор, пока не найдет [ символ. После этого он будет вызывать parseNode рекурсивно. После возврата узла разбора он будет смотреть на следующий символ, и если он равен ], это означает, что он проанализировал все дочерние элементы. Если нет, то символ должен был быть ;, и он должен съесть его и снова вызвать 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;
    }
}
2 голосов
/ 18 января 2011

В случае, если ваш формат может быть изменен (и может стать немного сложнее), вы можете рассмотреть возможность использования такого инструмента, как ANTLR , как предложил Toader.Тогда вам нужно только написать (или изменить) свою грамматику, чтобы создать (новый) лексер и парсер.Возьмите следующую грамматику:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...