1

我有一个字符串我想重写。该字符串包含看起来像“DDT”的子字符串加上四位数字。我将调用这些块。它还包含诸如“&”和“|”之类的连接词,其中| 表示“或”,以及括号。

现在我想重写这个字符串,使得用 &s 分隔的块应该写成“min(x(block1), x(block2), etc.)”,而用 |s 分隔的块应该写成“max(x (block1)、x(block2) 等)"。

看一个例子应该会有所帮助:

public class test{



 public static void main(String[] arg) throws Exception {


String str = "(DDT1453 & DDT1454) | (DDT3524 & DDT3523 & DDT3522 & DDT3520)";

System.out.println(str.replaceAll("DDT\\d+","x($0)"));

 }

}

我想要的输出是:

max(min(x(DDT1453),x(DDT1454)),min(x(DDT3524),x(DDT3523),x(DDT3522),x(DDT3520)))

如您所见,我执行了初始替换以包含输出的 x(block) 部分,但我无法获得其余部分。关于如何实现我想要的输出的任何想法?

4

5 回答 5

0

如果你有兴趣学习和使用 ANTLR

遵循 ANTLR 语法

grammar DDT;

options {
  output       = AST;
  ASTLabelType = CommonTree;
}

tokens {  DDT;  AMP;  PIPE;}

@members {}


expr  :  op1=amp (oper=PIPE^ op2=amp)*;
amp   :  op1=atom (oper=AMP^ op2=atom)*;
atom  :  DDT! INT   | '('! expr ')'!;

fragment

Digit  :  '0'..'9';
PIPE   :  '|'  ;
AMP    :  '&';
DDT    :  'DDT';
INT    :  Digit Digit*; 

生成低于 AST(抽象语法树)的输入(DDT1 | DDT2) & (DDT3 | DDT4) & DDT5

DDT 的 AST

上面的语法树 ( CommonTree) 可以按照预期的顺序(可选地使用 StringTemplates)进行遍历,并且可以获得所需的结果。

于 2013-08-19T22:32:42.713 回答
0

对于这么小的语法来说,一个成熟的解析器可能有点过头了,特别是当 OP 显然没有使用它们的经验时。即使使用像 ANTLR 或 JavaCC 这样的解析器生成器似乎也不是一个好主意。

用当前的信息来详细说明并不容易。OP,请提供所要求的信息作为对您问题的评论。

暂定语法:

maxExpr ::= maxExpr '|' '(' minExpr ')'
maxExpr ::= '(' minExpr ')'
minExpr ::= minExpr '&' ITEM
minExpr ::= ITEM
ITEM ::= 'DDT\d{4}'

意识到,确实,RegEx 的语法过多,但对于单个RegEx。没有人说我们不能使用多个。事实上,即使是最简单的 RegEx 替换也可以看作是图灵机中的一个步骤,因此使用它们可以解决问题。所以...

str= str.replaceAll("\\s+", "" ) ;
str= str.replaceAll("&", "," ) ;
str= str.replaceAll("\\([^)]+\\)", "-$0" ) ;
str= str.replaceAll("\\|", "," ) ;
str= str.replaceAll(".+", "+($0)" ) ;
str= str.replaceAll("\\w+", "x($0)" ) ;
str= str.replaceAll("\\+", "max" ) ;
str= str.replaceAll("-", "min" ) ;

我没有走太多捷径。一般的想法是,“+”等同于 的产生,maxExpr“-”等同于其中之一minExpr

我用输入测试了这个

str= "(DDT1453 & DDT1454 & DDT1111) | (DDT3524 & DDT3523 & DDT3522 & DDT3520)" ;

输出是:

max(min(x(DDT1453),x(DDT1454),x(DDT1111)),min(x(DDT3524),x(DDT3523),x(DDT3522),x(DDT3520)))

回到语法的概念,很容易认识到它的重要元素确实是ITEMS和'|' . 其余的(括号和'&')只是装饰。

简化语法:

maxExpr ::= maxExpr '|' minExpr
maxExpr ::= minExpr
minExpr ::= minExpr ITEM
minExpr ::= ITEM
ITEM ::= 'DDT\d{4}'

从这里,一个非常简单的有限自动机:

<start>
    maxExpr= new List() ;
    minExpr= new List() ;

"Expecting ITEM" (BEFORE_ITEM):
    ITEM -> minExpr.add(ITEM) ; move to "Expecting ITEM, |, or END"

"Expecting ITEM, |, or END" (AFTER_ITEM):
    ITEM -> minExpr.add(ITEM) ; move to "Expecting ITEM, |, or END"
    | -> maxExpr.add(minExpr); minExpr= new List(); move to "Expecting ITEM"
    END -> maxExpr.add(minExpr); move to <finish>

...以及相应的实现:

static Pattern pattern= Pattern.compile("(\\()|(\\))|(\\&)|(\\|)|(\\w+)|(\\s+)") ;
static enum TokenType { OPEN, CLOSE, MIN, MAX, ITEM, SPACE, _END_, _ERROR_ };
static enum State { BEFORE_ITEM, AFTER_ITEM, END }

public static class Token {
    TokenType type;
    String value;
    public Token(TokenType type, String value) {
        this.type= type ;
        this.value= value ;
    }
}
public static class Lexer {
    Scanner scanner;
    public Lexer(String input) {
        this.scanner= new Scanner(input) ;
    }
    public Token getNext() {
        String tokenValue= scanner.findInLine(pattern) ;
        TokenType tokenType;
        if( tokenValue == null ) tokenType= TokenType._END_ ;
        else if( tokenValue.matches("\\s+") ) tokenType= TokenType.SPACE ;
        else if( "(".equals(tokenValue) ) tokenType= TokenType.OPEN ;
        else if( ")".equals(tokenValue) ) tokenType= TokenType.CLOSE ;
        else if( "&".equals(tokenValue) ) tokenType= TokenType.MIN ;
        else if( "|".equals(tokenValue) ) tokenType= TokenType.MAX ;
        else if( tokenValue.matches("\\w+") ) tokenType= TokenType.ITEM ;
        else tokenType= TokenType._ERROR_ ;
        return new Token(tokenType,tokenValue) ;
    }
    public void close() {
        scanner.close();
    }
}
public static String formatColl(String pre,Collection<?> coll,String sep,String post) {
    StringBuilder result= new StringBuilder() ;
    result.append(pre);
    boolean first= true ;
    for(Object item: coll ) {
        if( ! first ) result.append(sep);
        result.append(item);
        first= false ;
    }
    result.append(post);
    return result.toString() ;
}
public static void main(String... args) {

    String str= "(DDT1453 & DDT1454) | (DDT3524 & DDT3523 & DDT3522 & DDT3520)" ;
    Lexer lexer= new Lexer(str) ;
    State currentState= State.BEFORE_ITEM ;
    List<List<String>> maxExpr= new LinkedList<List<String>>() ;  
    List<String> minExpr= new LinkedList<String>() ;  
    while( currentState != State.END ) {
        Token token= lexer.getNext() ;
        switch( currentState ) {
        case BEFORE_ITEM:
            switch( token.type ) {
            case ITEM:
                minExpr.add("x("+token.value+")") ;
                currentState= State.AFTER_ITEM ;
                break;
            case _END_:
                maxExpr.add(minExpr) ;
                currentState= State.END ;
                break;
            default:
                // Ignore; preserve currentState, of course
                break;
            }
            break;
        case AFTER_ITEM:
            switch( token.type ) {
            case ITEM:
                minExpr.add("x("+token.value+")") ;
                currentState= State.AFTER_ITEM ;
                break;
            case MAX:
                maxExpr.add(minExpr) ;
                minExpr= new LinkedList<String>() ;
                currentState= State.BEFORE_ITEM ;
                break;
            case _END_:
                maxExpr.add(minExpr) ;
                currentState= State.END ;
                break;
            default:
                // Ignore; preserve currentState, of course
                break;
            }
            break;
        }
    }
    lexer.close();

    System.out.println(maxExpr);

    List<String> maxResult= new LinkedList<String>() ;
    for(List<String> minItem: maxExpr ) {
        maxResult.add( formatColl("min(",minExpr,",",")") ) ;
    }

    System.out.println( formatColl("max(",maxResult,",",")") );
}
于 2013-08-19T21:10:16.867 回答
0

只是进行字符串替换是错误的方法。改用递归下降解析

首先,您要定义什么符号创建什么,例如:

程序 -> LiteralArg|fn(x)|程序

LiteralArg -> LiteralArg

LiteralArg&LiteralArg -> fn(LiteralArg) & fn'(LiteralArg)

fn(x) -> fn(x)

fn(x) |fn(y) -> fn(x),fn(y)

从那里您可以创建函数,这些函数将递归地解析您的数据,期望某些事情会发生。例如

 String finalResult = "";
 function parse(baseString) {
     if(basestring.isLiteralArg)
     {
         if(peekAheadToCheckForAmpersand())
         {
              expectAnotherLiteralArgAfterAmpersandOtherwiseThrowError();
              finalResult += fn(LiteralArg) & fn'(LiteralArg)
              parse(baseString - recentToken);
         }
         else
         {
             finalResult += literalArg;
              parse(baseString - recentToken);
         }
     }
     else if(baseString.isFunction()
      {
           if(peekAheadToCheckForPipe())
           {
              expectAnotherFunctionAfterAmpersandOtherwiseThrowError();
                finalResult += fn(x),fn(y)
                 parse(baseString - recentToken);
           }
           else
           {
               finalResult += fn(x)
               parse(baseString - recentToken);
           }
      }

}

当您找到标记时,将它们从字符串中取出并在剩余的字符串上调用 parse 函数。

我基于几年前做的一个项目的粗略示例。以下是相关讲座: http ://faculty.ycp.edu/~dhovemey/fall2009/cs340/lecture/lecture7.html

于 2013-08-19T20:35:46.467 回答
0

如果您坚持使用正则表达式替换,那么以下代码似乎有效:

str = str.replaceAll("\\([^)]*\\)", "min$0");
str = str.replaceAll("DDT\\d+","x($0)");
str = str.replaceAll("&|\\|",",");
str = "max(" + str + ")";

但是,我会考虑其他人的建议-改用解析逻辑。这样,您将来可以轻松地扩展语法,还可以验证输入并报告有意义的错误消息。

- 编辑 -

上面的解决方案假设没有嵌套。如果嵌套是合法的,那么你绝对不能使用正则表达式解决方案。

于 2013-08-19T21:47:28.623 回答
-1

正则表达式不是这样做的最佳选择 - 或者马上说:它不可能(在 java 中)。

正则表达式可能能够使用反向引用来更改给定字符串的格式,但它不能生成内容感知反向引用。换句话说:您将需要某种递归(或迭代解决方案)来解决嵌套括号的无限深度。

因此,您需要编写自己的解析器,它能够处理您的输入。

虽然用DDT1234适当的表示替换字符串x(DDT1234)很容易(它是所有事件的单一反向引用),但您需要自己注意正确的嵌套。

对于解析嵌套表达式,您可能想看看这个例子: Parsing an Infix Expression with Parentheses (like ((2*4-6/3)*(3*5+8/4))-(2+3 )) http://www.smccd.net/accounts/hasson/C++2Notes/ArithmeticParsing.html

它只是如何处理这样一个给定字符串的(口头)示例。

于 2013-08-19T22:03:53.660 回答