重写字符串的一部分

时间:2013-08-19 20:14:55

标签: java string

我有一个我想重写的字符串。该字符串包含看起来像“DDT”加上四位数的子字符串。我会打电话给这些街区。它还包含像“&”这样的连接词和“|”,其中|代表“或”,以及括号。

现在我想重写这个字符串,以便用& s分隔的块应该写成“min(x(block1),x(block2)等)”,而用| s分隔的块应该写as“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(块)部分,但我无法完成其余部分。关于如何实现我想要的输出的任何想法?

5 个答案:

答案 0 :(得分: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);
           }
      }

}

当您找到令牌时,将它们从字符串中取出并在剩余字符串上调用解析函数。

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

答案 1 :(得分: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,",",")") );
}

答案 2 :(得分:0)

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

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

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

- 编辑 -

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

答案 3 :(得分: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*; 

为输入(DDT1 | DDT2) & (DDT3 | DDT4) & DDT5

生成AST(抽象语法树)

AST for DDT

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

答案 4 :(得分:-1)

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

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

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

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

要解析嵌套表达式,您可能需要查看此示例: 用括号解析中缀表达式(如((2 * 4-6 / 3)*(3 * 5 + 8/4)) - (2 + 3)) http://www.smccd.net/accounts/hasson/C++2Notes/ArithmeticParsing.html

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