【问题标题】:Java: Parsing a string representation into a tree representaitonJava:将字符串表示解析为树表示
【发布时间】:2023-03-21 07:06:03
【问题描述】:

我需要使用 Java,所以不能使用脚本语言支持。

我需要解析的字符串表示如下:

op1 (t1,t2,t3,...)

其中 t1、t2、t3 等也可以是 op2 (t11,t12,t13 ...) 之类的东西,或者只是一个原子单位(本身不能由元素组成)

一个具体的例子是:

op1 (op2 (t1 t2) t3)

我想把它解析成树状结构(分层)

op1
 op2
  t1
  t2
 t3

假设 op1 是树的根,op2 是 op1 的左子孩子,t3 是 op1 的右子孩子。 t1 和 t2 分别是 op2 的子子项。

如何在 Java 中做到这一点?具有挑战性的部分是生成的树不能是二叉树。一个节点可以有任意数量的子节点。

【问题讨论】:

  • 嘿鲍勃,这是作业吗?最好有homework 标签。
  • @digitaljoel homework 标签已被弃用。如果您有兴趣,请检查元堆栈溢出以获取详细信息。还是一样,不能再用了。
  • 有趣的@BlackVegetable 我没见过。感谢您指出。对于其他感兴趣的人,这里是元帖子的链接meta.stackexchange.com/questions/147100/…
  • @Bob What have you tried? 你对字符串解析了解多少?您在另一条评论中提到您不能使用第三方库,这意味着您需要从头开始构建它。解释有关如何做到这一点的所有内容将比我们这里的大多数人花费更多的时间。通过询问一些具体的问题来帮助我们帮助您,了解您尝试过的方法以及您在此过程中遇到的问题。

标签: java string parsing tree representation


【解决方案1】:

如果您不能使用 JavaCC,那么您可以查看 StringTokenizer 类。你可以在几次通行证中完成。首先,对括号进行标记,创建一个第一遍树。然后,您可以遍历树并在空间上标记化,为那些只有叶子而没有嵌套树的节点进一步充实树节点(即没有包含括号的子节点)

op1 (op2 (t1 t2) t3) 在 '(' 和 ')' 上进行标记时会给出标记(假设您要求包含标记)op1, (, op2, (, t1 t2, ), t3, ) 从中您可以遍历标记。你知道你的第一个是父母。其次是父母,所以你知道你有一个复杂的孩子。所以你的树是:

op1
  op2

然后你又打了一个paren,意思是一个新的复杂的孩子。第二个开放括号之后的下一个标记是t1 t2,所以你的树是

op1
  op2
    t1 t2

然后你得到一个接近的括号标记,所以你结束了 op2 的复杂子代,你的下一个标记是 op1 的下一个子代​​,这意味着你的树看起来像

op1
  op2
    t1 t2
t3

最后你碰到了最后一个闭括号,它结束了 op1 的复杂子节点。

现在您可以遍历树,在空间上拆分子节点。第一个节点是 op1,所以没有分裂,与 op2 相同。 't1 t2' 分裂成 't1' 和 't2' 所以你最终将该节点分成两个所以你的树看起来像

op1
  op2
    t1
    t2
  t3

您可能很容易将空间分割放入第一种方法中,这样您就不必遍历树两次。

【讨论】:

  • 你能告诉我一些处理括号(子)或空格(同一级别的成员)的具体代码吗?然后我可以接受并完成。我很难用 Java 思考。我使用了函数式编程语言,我会在直接递归函数中做到这一点。我的问题是如何找出 op1 的子代由 op2 (t1 t2) 和 t3 组成?找出这个破位是我唯一解决不了的。
  • 感谢您的解释。我正在阅读 StringTokenizer 类。似乎它非常强大。以前没用过这个。
  • 问题已编辑,包含有关算法的更多信息。
  • @Bob Java 支持递归。离开计算机一会儿,用文字描述你将如何解析字符串。这应该可以帮助您弄清楚如何在 Java(或任何其他编程语言)中做到这一点。
【解决方案2】:

一般来说,这种解析器很容易用JavaCC创建,只需创建简单的语法(做一些研究的主题——看看这个link

【讨论】:

  • 问题是我不允许使用任何第三方库。我需要提供原生 java。你能帮我解决这个问题吗?
  • javacc 会为你生成 Java 源码,所以你只需要在代码中添加一些“肉”即可。
  • 如果我可以自己编写这些 java 源代码会很好:-) 我确实编写了用于生成二叉树表示的解析器。但是在这种情况下,一个节点的子节点数量是任意的,这使得它很难解决。
【解决方案3】:

只是为了好玩,一个非常快速且非常肮脏且可证明有问题的解决方案。嘿,至少它解决了你的例子!!!!

package parser;

import java.util.Collection;
import java.util.ArrayList;
import java.util.Arrays;

/**
 *
 * @author gpeche
 */
public class Parser {

    private static abstract class Node {
        String name;

        String getName() { return name; }

        abstract boolean isComposite();

        Node(String name) { this.name = name; }        
    }

    private static class PrimitiveNode extends Node { 
        @Override boolean isComposite() { return false; }

        PrimitiveNode(String name) { super(name); }

        @Override public String toString() { return "PrimitiveNode(\"" + name + "\")"; }        
    }

    private static class CompositeNode extends Node { 
        Collection<Node> children = new ArrayList<Node>();

        void addChild(Node childNode) { children.add(childNode); }

        Collection<Node> getChildren() { return new ArrayList<Node>(children); }

        @Override boolean isComposite() { return false; }

        CompositeNode(String name) { super(name); }

        @Override public String toString() { 
            StringBuilder sb = new StringBuilder();
            sb.append("CompositeNode(\"");
            sb.append(name);
            sb.append("\") { ");
            boolean isFirstNode = true;
            for (Node node: children) {
                if (!isFirstNode) {
                    sb.append(", ");
                }
                sb.append(node.toString());
                isFirstNode = false;                   
            }
            sb.append(" }");
            return sb.toString();
        }        
    }

    // Parser state
    int pos = 0;

    String[] tokens;

    static final String OPENING_PAR = "(";
    static final String CLOSING_PAR = ")";
    static final String OP_PLUS = "+";
    static final String OP_MINUS = "-";
    static final String OP_MULTIPLY = "*";
    static final String OP_DIVIDE = "/";
    static final String OP_MODULUS = "mod";
    static final String OP_INT_DIVIDE = "div";

    static final Collection<String> PARENTHESIS = Arrays.asList(OPENING_PAR, CLOSING_PAR);

    static final Collection<String> OPERATIONS = Arrays.asList( OP_PLUS,
                                                                OP_MINUS,
                                                                OP_MULTIPLY,
                                                                OP_DIVIDE,
                                                                OP_MODULUS,
                                                                OP_INT_DIVIDE);


    public Node parse(String treeRepresentation) {
        tokenize(treeRepresentation);
        return parseNode();
    }

    private void tokenize(String treeRepresentation) {
        treeRepresentation = treeRepresentation.replace("(", " ( ");
        treeRepresentation = treeRepresentation.replace(")", " ) ");
        tokens = treeRepresentation.split("\\s+");
    }

    private Node parseNode() {
        // check that current token is not a "syntax" token
        String currentToken = tokens[pos];
        if (PARENTHESIS.contains(currentToken)) {
            throw new IllegalArgumentException(String.format("Invalid token %d, expected identifier. got %s", pos + 1, currentToken));
        }

        boolean isComposite = currentToken != null && 
                              (currentToken.startsWith("op") ||  // Accept identifiers as operations (function support :P)
                               OPERATIONS.contains(currentToken));
        return isComposite? parseComposite() : parsePrimitive();
    }

    private Node parseComposite() {
        CompositeNode composite = new CompositeNode(tokens[pos]);
        pos++;                

        if (!OPENING_PAR.equals(tokens[pos])) {
            throw new IllegalArgumentException(String.format("Invalid token %d, expected '(', got %s", pos + 1, tokens[pos]));
        } else {
            // Ignore opening parenthesis
            pos++;
        }

        boolean nextIsIdentifier;
        do {
            composite.addChild(parseNode());
            nextIsIdentifier = !PARENTHESIS.contains(tokens[pos]);            
        } while (nextIsIdentifier);

        if (!CLOSING_PAR.equals(tokens[pos])) {
            throw new IllegalArgumentException(String.format("Invalid token %d, expected ')', got %s", pos + 1, tokens[pos]));
        } else {
            pos++;
        }

        return composite;
    }

    private Node parsePrimitive() {
        // Create primitive node and advance position
        Node result = new PrimitiveNode(tokens[pos]);
        pos++;
        return result; 
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Parser parser = new Parser();
        Node parsedNode = parser.parse("op1 (op2 (t1 t2) t3)");
        System.out.println(parsedNode.toString());
    }
}

【讨论】:

    【解决方案4】:

    首先,我建议您将此问题拆分为两个子问题,第一个 - 解析输入,第二个 - 创建树。

    您可以对第一个使用字符串标记器和堆栈,您可以跳过“op1”,因为通常您的问题看起来像 ((t1 t2) t3)。因此,当您当前的元素将是 = ")" 时,您应该从堆栈中弹出元素,直到您到达 "(" 并从该元素中创建新节点并将其放回堆栈。另一个问题是您的数据类型是什么将存储在堆栈中,显然它不会是字符串,所以可能您必须创建一些可以放置在那里的元素层次结构,例如 StringElement、NodeElement、StartQuoteElement。

    【讨论】:

      猜你喜欢
      • 2021-04-04
      • 2012-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 2010-12-21
      • 2017-10-08
      相关资源
      最近更新 更多