2013-08-19 3 views
1

У меня есть строка, которую я бы хотел переписать. Строка содержит подстроки, которые выглядят как «DDT» плюс четыре цифры. Я назову эти блоки. Он также содержит такие связки, как «&» и «|», где | представляет «или», а также круглые скобки.Переписывание частей строки

Теперь я хотел бы переписать эту строку так, чтобы блоки, разделенные &, должны быть записаны как «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))) 

Как вы видите, я выполнил первоначальную замену, включив в него х (блоков) часть выход, но я не могу получить все остальное. Любые идеи о том, как достичь желаемого результата?

+3

Остерегайтесь уязвимостей к инъекциям. – SLaks

+1

Ваш желаемый результат не соответствует вашему описанию. Вы случайно заменили «max» и «min» на первые два экземпляра, или ваше описание не так? –

+0

Да, спасибо, просто изменил его – kjm

ответ

0

Простое замещение строк - это неправильный способ сделать это. Использование рекурсивного спуска Синтаксический вместо

Сначала вы хотите, чтобы определить, что создавать символы, что, например:

программа -> LiteralArg | п (х) | Программа

LiteralArg -> LiteralArg

LiteralArg & LiteralArg -> п (LiteralArg) & п '(LiteralArg)

п (х) -> п (х)

п (х) | п (у) -> п (х), п (у)

Оттуда вы делаете функции, которые будут рекурсивно синтаксический анализ данных, ожидающие некоторые вещи случаться. Например,

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

Полномасштабный синтаксический анализатор для такой маленькой грамматики может быть излишним, особенно когда ОП, очевидно, не имеет опыта с ними. Даже использование генераторов парсеров, таких как 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))) 

Вернуться к идее грамматики, легко признать, что существенные элементы его действительно ПУНКТЫ и «| ' , Все остальное (круглые скобки и «&») - это просто украшение.

Упрощенная грамматика:

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

Если вы настаиваете на использовании regex, то следующий код работает:

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

Hoewever, я считал бы, что другие предполагают - с помощью логики синтаксического анализа вместо этого. Таким образом, вы можете легко расширить свою грамматику в будущем, а также сможете проверять ввод и сообщать о значимых сообщениях об ошибках.

--EDIT--

Решение выше, предполагает, что нет вложенности. Если вложение является законным, то вы определенно не можете использовать решение регулярного выражения.

-1

Regex - не лучший выбор для этого - или сказать сразу: его невозможно (в java).

Regex может изменить форматирование данной строки, используя backreferenes, но не может генерировать содержание известно обратные ссылки. Другими словами: вам потребуется какая-то рекурсия (или итеративное решение) для разрешения бесконечной глубины вложенных скобок.

Следовательно, вам нужно будет написать собственный синтаксический анализатор, который сможет обрабатывать ваши данные.

При замене строк DDT1234 строки с соответствующим представлением x(DDT1234) легко выполнимы (его единственная обратная ссылка для ВСЕХ вхождений), вам нужно позаботиться о правильной вложенности самостоятельно.

Для разбора вложенных выражений, вы можете захотеть взглянуть на этот пример: разборе Expression Инфиксные со скобками (как ((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 желаемый результат может быть получен.

Смежные вопросы