我有一个我想重写的字符串。该字符串包含看起来像“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(块)部分,但我无法完成其余部分。关于如何实现我想要的输出的任何想法?
答案 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
上面的语法树(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
它只是一个(口头)如何处理这样一个给定字符串的例子。