2013-08-19 31 views
1

我有一個字符串,我想重寫。該字符串包含看起來像「DDT」加上四位數字的子字符串。我會打電話給這些塊。它還包含連接詞,如「&」和「|」,其中|代表「或」,以及括號。重寫字符串的部分

現在我想重寫這個字符串,使得由& s分隔的塊應該寫爲「min(x(block1),x(block2)等)」,而由| 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(塊)的一部分輸出,但我無法得到其餘的。關於如何實現我想要的輸出的任何想法?

+3

注意注入漏洞。 – SLaks

+1

您所需的輸出與您的描述不符。你在前兩次意外交換了'max'和'min'還是你的描述錯誤? –

+0

是的,謝謝,只是改變了它 – kjm

回答

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

0

這樣一個小語法的成熟的解析器可能是一個矯枉過正的問題,特別是當OP顯然沒有先前的經驗時。甚至不使用像ANTLR或JavaCC這樣的解析器生成器似乎是一個好主意。

用當前信息來闡述更多信息並不容易。 OP,請提供所需信息作爲對您問題的評論。

暫定語法:

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

意識到,true,則語法是過度用於正則表達式,但是對於單個正則表達式。沒有人說我們不能使用多於一個。事實上,即使是最簡單的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))) 

返回語法的想法,很容易認識到它的顯著元素真的是項和「| 「 。其餘的(括號和'&')只是裝飾。

簡化語法:

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,",",")")); 
} 
0

如果你堅持使用正則表達式替換,那麼下面的代碼似乎工作:

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

Hoewever,我會考慮別人的建議 - 使用解析邏輯來代替。 通過這種方式,您可以在將來輕鬆擴展您的語法,並且您還可以驗證輸入並報告有意義的錯誤消息。

- EDIT--

上面的解決方案假定沒有嵌套。如果嵌套是合法的,那麼你絕對不能使用正則表達式解決方案。

-1

正則表達式不是做這件事的最佳選擇 - 或馬上說出來:它不可能(在java中)。

正則表達式可能能夠使用反向引用來更改給定字符串的格式化,但它不能生成內容識別反向引用。換句話說:您需要某種遞歸(或迭代解決方案)來解決嵌套括號的無限深度。

因此,您需要編寫自己的解析器,它能夠處理您的輸入。

雖然用適當的x(DDT1234)表示替換DDT1234字符串很容易實現(它對所有出現的單個反向引用),但您需要自行考慮正確的嵌套。解析嵌套表達式,你可能想看看這個例子: 用括號解析中綴表達式(如((2 * 4-6/3)*(3 * 5 + 8/4) ) - (2 + 3)) http://www.smccd.net/accounts/hasson/C++2Notes/ArithmeticParsing.html

它只是一個(口頭)如何處理這種給定的字符串的例子。

0

如果您intested學習和使用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

AST for DDT

上述語法樹(CommonTree)可按預定順序(可選地使用StringTemplates)和d可以獲得期望的結果。