【问题标题】:Split a string with nested parenthesis on multiple tokens在多个标记上拆分带有嵌套括号的字符串
【发布时间】:2017-08-01 05:11:40
【问题描述】:

我正在尝试拆分 ||&&() 上的字符串,但无法正确拆分嵌套括号。

示例字符串:

q1 == false || ( q1 == true && q3 != null && ( method(param) - method() ) > 120 )

我当前的正则表达式/代码:

String[] tempTokens = input.split("(?=([|]{2}|[&]{2}|[(]|[)]))|(?<=([|]{2}|[&]{2}|[(]|[)]))");

for (String token : tempTokens) {
    if (token.trim().length() > 0) {
        System.out.println(token.trim());
    }
}

当前输出:

q1 == false
||
(
q1 == true
&&
q3 != null
&&
(
method
(
param
)
- method
(
)
)
> 120
)

想要的输出:

q1 == false
||
(
q1 == true
&&
q3 != null
&&
( method(param) - method() ) > 120
)

基本上,我正在尝试对表达式进行标记,并且仅当它们包含包含&gt;&gt;=== 等的完整语句时,我想在括号上拆分。

【问题讨论】:

  • Java 正则表达式引擎不具备匹配平衡文本(嵌套)的能力。所以,它不能匹配像 ( method(param) - method() ) 这样的东西
  • 这将是对正则表达式的滥用,你最终会写出一个很长的、可能有问题的表达式。正则表达式用于正则文法。您显示的代码是上下文无关的。查看Regular VS Context-Free GrammarsParsing If Statement with Regex
  • 如果正则表达式看起来很复杂,那么您可能会在下一次代码重写中丢失它,因为下一个开发人员无法理解逻辑,或者您可能会产生时间复杂度问题(我有看到这种情况发生)。这意味着您应该研究如何简化正则表达式或找到一种不同的方法来标记字符串。在您的情况下,您似乎正在处理语言语法,因此可能需要一个简单的“有限状态机”?
  • 我已经在使用 Shunting-Yard 算法将这些表达式解析成一棵树,但问题是我想将每个问题和答案 (q1 == false) 一起保存在一个节点中,并且是唯一的关系将是&amp;&amp;||,所以我的算法只有&amp;&amp;|| 作为运算符。因此,我正在尝试将字符串拆分为适当的标记以忽略所有其他运算符是否容易。我将尝试修改我的代码以解析所有运算符,看看是否可行。
  • 您的要求非常具体。一个简单的正则表达式将不符合它。我认为最好定义一个带有规则的解析器。

标签: java regex


【解决方案1】:

谢谢大家的建议!在不使用正则表达式的情况下解析表达式肯定更容易。

我使用修改后的 Shutting-yard 算法将表达式转换为后缀符号,然后从中构建一棵树。

public class ExpressionTreeNode {

    private ExpressionTreeNode left, right;
    private String content;

    public ExpressionTreeNode(ExpressionTreeNode left, ExpressionTreeNode right, String content) {
        this.left = left;
        this.content = content;
        this.right = right;
    }
}

创建树方法:

private static ExpressionTreeNode createExpressionTree(String[] tokens) {
    final Stack<ExpressionTreeNode> nodes = new Stack<ExpressionTreeNode>();

    for (int i = 0; i < tokens.length; i++) {
        String token = tokens[i];

        if (Operator.isOperator(token)) {
            ExpressionTreeNode rightNode = nodes.pop();
            ExpressionTreeNode leftNode = nodes.pop();
            nodes.push(new ExpressionTreeNode(leftNode, rightNode, token));
        } else {
            nodes.add(new ExpressionTreeNode(null, null, token));
        }
    }

    return nodes.pop();
}

及用法:

// here convert to postfix notation
String[] tempTokens = part.split(" ");
String[] output = infixToPostfix(tempTokens);

ExpressionTreeNode root = createExpressionTree(output);

这些树非常大,因为所有操作(不仅仅是||&amp;&amp;)都分成了节点,但它对我有用。

问题示例的输出:

                 /----- 120
         /----- >
         |       |       /----- method()
         |       \----- -
         |               \----- method(param)
 /----- &&
 |       |               /----- null
 |       |       /----- !=
 |       |       |       \----- q3
 |       \----- &&
 |               |       /----- true
 |               \----- ==
 |                       \----- q1
||
 |       /----- false
 \----- ==
         \----- q1

【讨论】:

    【解决方案2】:

    我认为您无法使用正则表达式正确解析此类字符串。我希望您想要评估此类字符串的某些部分。这就是您可能想要查找 AST - 抽象语法树,解析器生成器的地方。对于С 语言,它是lex + yacc。对于 java,它是 antlr 或 javacc。我已经处理了所有这些。

    【讨论】:

      【解决方案3】:

      也许使用模式/匹配器可以让你接近

              String txt="( method(param) - method() ) >= 120";
      
          String re1="(\\()"; // Single Character (
          String re2="(\\s+)";    // White Space 1
          String re3="((?:[a-z][a-z0-9_]*))"; // Variable Name method
          String re4="(\\()"; //  Single Character (
          String re5="((?:[a-z][a-z0-9_]*))"; // Variable Name param
          String re6="(\\))"; // Single Character )
          String re7="( )";   // White Space 
          String re8="(-)";   // Single Character -
          String re9="( )";   // White Space 
          String re10="((?:[a-z][a-z0-9_]*))";    // Variable Name method
          String re11="(\\()";    //  Single Character (
          String re12="(\\))";    //  Single Character )
          String re13="( )";  // White Space 
          String re14="(\\))";    //  Single Character )
          String re15="( )";  // White Space
          String re16="(..?)";    // Any 1 or 2 Characters > >= ==
          String re17="( )";  // White Space 
          String re18="(\\d+)";   // Integer 120 
      
          Pattern p = Pattern.compile(re1+re2+re3+re4+re5+re6+re7+re8+re9+re10+re11+re12+re13+re14+re15+re16+re17+re18,Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
          Matcher m = p.matcher(txt);
          if (m.find())
          {
              String c1=m.group(1);
              String ws1=m.group(2);
              String var1=m.group(3);
              String c2=m.group(4);
              String var2=m.group(5);
              String c3=m.group(6);
              String ws2=m.group(7);
              String c4=m.group(8);
              String ws3=m.group(9);
              String var3=m.group(10);
              String c5=m.group(11);
              String c6=m.group(12);
              String ws4=m.group(13);
              String c7=m.group(14);
              String ws5=m.group(15);
              String c8=m.group(16);
              String ws6=m.group(17);
              String int1=m.group(18);
              System.out.println(c1.toString() + ws1.toString() + var1.toString() + c2.toString() + var2.toString() +c3.toString()+ws2.toString()+c4.toString()+ws3.toString()+var3.toString()+c5.toString()+c6.toString()+ws4.toString()+c7.toString()+ws5.toString()+c8.toString()+ws6.toString()+int1.toString()+"\n");
          }
      

      【讨论】:

        【解决方案4】:

        对于您非常特殊的解决方案,您可以使用这个:(编辑,更好的编码)

            var s = "q1 == false || ( q1 == true && q3 != null && ( method(param) - method() ) > 120 )";
            var result = new List<string>();
            Parse(result, s);
        
        
            void Parse(List<string> result, string s)
            {
                var currentSplitter = string.Empty;
                var listOfSplitters = new string[] { "||", "&&" };
                var splitterPosition = -1;
        
                for (var i = 0; i < listOfSplitters.Length; i++)
                {
                    currentSplitter = listOfSplitters[i];
                    splitterPosition = s.IndexOf(currentSplitter);
                    if (splitterPosition > -1)
                    {
                        break;
                    }
                }
        
                if (splitterPosition == -1) //not found
                {
                    result.Add(s);
                    return;
                }
        
                var parenthesisPosition = s.IndexOf("(");
                if (parenthesisPosition == -1) //no more parenthesis
                {
                    result.Add(s);
                }
                else if(parenthesisPosition > splitterPosition) //there is a parenthesis, but it's after a splitter
                {
                    result.Add(s.Substring(0,splitterPosition-1));
                    result.Add(currentSplitter);
                    Parse(result, s.Substring(splitterPosition + currentSplitter.Length, s.Length - splitterPosition - currentSplitter.Length));
                }
                else //there is a parenthesis
                {
                    result.Add("(");
                    var lastpar = s.LastIndexOf(')');
                    var sub = s.Substring(parenthesisPosition+1, lastpar -2);
                    Parse(result, sub);
                    result.Add(")");
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-03-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-08
          • 2014-02-26
          相关资源
          最近更新 更多