表达式求值:
30+3*(4-2.3)-3
这个式子,请问该怎么正确分解成为运算符和操作数?
我有个基本想法是:用DataInputStream来读一个byte,判断是否是数字,如果是,则放回流里面,然后以double形式重新读入一个。如果是字符,则是正常操作符。
如此直至读到末尾。
但这样做,我不知道怎么样把已经读入的字符放回流里面。请教大家一个思路,谢谢!
30+3*(4-2.3)-3
这个式子,请问该怎么正确分解成为运算符和操作数?
我有个基本想法是:用DataInputStream来读一个byte,判断是否是数字,如果是,则放回流里面,然后以double形式重新读入一个。如果是字符,则是正常操作符。
如此直至读到末尾。
但这样做,我不知道怎么样把已经读入的字符放回流里面。请教大家一个思路,谢谢!
使用这个类:PushbackInputStream 中的方法read()!
TO:believefym 对,不过运算符当然包括左右括号谢谢两位
再看下有没有别的完全不同的思路。
其实还有scanner等类可以使用,但我不太清楚
String regex = "(?<=[+/*()-])|(?=[+/*()-])";
String[] ss = s.split(regex);for (int i = 0; i < ss.length; i++)
{
System.out.println(ss[i]);
}
package TAP;import java.util.Stack;
import java.lang.Character;
/**
* Reverse Polish Notation
* 算术逆波兰表达式.生成.
* @author ewa26
*
*/
public class SimpleRPN
{
private SimpleRPN() { }
@SuppressWarnings("unchecked")
private static String BuildingRPN(String s)
{
StringBuilder sb = new StringBuilder(s);
Stack sk = new Stack();
StringBuilder re = new StringBuilder();
char c = ' ';
//sb.Replace(" ","");//一开始,我只去掉了空格.后来我不想不支持函数和常量能滤掉的全OUT掉.
for (int i = 0; i < sb.length(); i++)
{
c = sb.charAt(i);
if (Character.isDigit(c))//数字当然要了.
re.append(c);
//if(char.IsWhiteSpace(c)||char.IsLetter(c))//如果是空白,那么不要.现在字母也不要.
//continue;
switch (c)//如果是其它字符...列出的要,没有列出的不要.
{
case '+':
case '-':
case '*':
case '/':
case '%':
case '^':
case '!':
case '(':
case ')':
case '.':
re.append(c);
break;
default:
continue;
}
}
sb = new StringBuilder(re.toString());
//#region 对负号进行预转义处理.负号变单目运算符求反.
for (int i = 0; i < sb.length() - 1; i++)
if (sb.charAt(i) == '-' && (i == 0 || sb.charAt(i - 1) == '('))
sb.setCharAt(i, '!');//字符转义.
// #endregion
// #region 将中缀表达式变为后缀表达式.
for (int i = 0; i < sb.length(); i++)
{
if (Character.isDigit(sb.charAt(i)) ||sb.charAt(i) == '.')//如果是数值.
{
re.append(sb.charAt(i));//加入后缀式
}
else if (sb.charAt(i) == '+'
|| sb.charAt(i) == '-'
|| sb.charAt(i) == '*'
|| sb.charAt(i) == '/'
|| sb.charAt(i)== '%'
|| sb.charAt(i) == '^'
|| sb.charAt(i) == '!')//.
{
// #region 运算符处理
while (sk.size() > 0) //栈不为空时
{
c = sk.pop().toString().charAt(0); //将栈中的操作符弹出.
if (c == '(') //如果发现左括号.停.
{
sk.push(c); //将弹出的左括号压回.因为还有右括号要和它匹配.
break; //中断.
}
else
{
if (Power(c) < Power(sb.charAt(i)))//如果优先级比上次的高,则压栈.
{
sk.push(c);
break;
}
else
{
re.append(' ');
re.append(c);
}
//如果不是左括号,那么将操作符加入后缀式中.
}
}
sk.push(sb.charAt(i)); //把新操作符入栈.
re.append(' ');
// #endregion
}
else if (sb.charAt(i) == '(')//基本优先级提升
{
sk.push('(');
re.append(' ');
}
else if (sb.charAt(i) == ')')//基本优先级下调
{
while (sk.size() > 0) //栈不为空时
{
c = sk.pop().toString().charAt(0); //pop Operator
if (c != '(')
{
re.append(' ');
re.append(c);//加入空格主要是为了防止不相干的数据相临产生解析错误.
re.append(' ');
}
else
break;
}
}
else
re.append(sb.charAt(i));
}
while (sk.size() > 0)//这是最后一个弹栈啦.
{
re.append(' ');
re.append(sk.pop());
}
// #endregion re.append(' ');
return FormatSpace(re.toString());//在这里进行一次表达式格式化.这里就是后缀式了.
} /// <summary>
/// 算术逆波兰表达式计算.
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
@SuppressWarnings("unchecked")
public static String ComputeRPN(String s)
{ String S = BuildingRPN(s);
String tmp = "";
Stack sk = new Stack();
char c = ' ';
StringBuilder Operand = new StringBuilder();
double x, y;
for (int i = 0; i < S.length(); i++)
{
c = S.charAt(i);
if (Character.isDigit(c) || c == '.')
{//数据值收集.
Operand.append(c);
}
else if (c == ' ' && Operand.length() > 0)
{
// #region 运算数转换
try
{
tmp = Operand.toString();
if (tmp.startsWith("-"))//负数的转换一定要小心...它不被直接支持.
{//现在我的算法里这个分支可能永远不会被执行.
sk.push(-((double)Double.parseDouble(tmp.substring(1, tmp.length() - 1))));
}
else
{
sk.push(Double.parseDouble(tmp));
}
}
catch (Exception e)
{
return "发现异常数据值.";
}
Operand = new StringBuilder();
// #endregion
}
|| c == '-'
|| c == '*'
|| c == '/'
|| c == '%'
|| c == '^')
{
// #region 双目运算
if (sk.size() > 0)/*如果输入的表达式根本没有包含运算符.或是根本就是空串.这里的逻辑就有意义了.*/
{
y = Double.parseDouble(sk.pop().toString());
}
else
{
sk.push(0);
break;
}
if (sk.size() > 0)
x = Double.parseDouble(sk.pop().toString());
else
{
sk.push(y);
break;
}
switch (c)
{
case '+':
sk.push(x + y);
break;
case '-':
sk.push(x - y);
break;
case '*':
sk.push(x * y);
break;
case '/':
sk.push(x / y);
break;
case '%':
sk.push(x % y);
break;
case '^':
// if(x>0)
// {我原本还想,如果被计算的数是负数,又要开真分数次方时如何处理的问题.后来我想还是算了吧.
sk.push(Math.pow(x, y));
// }
// else
// {
// double t=y;
// string ts="";
// t=1/(2*t);
// ts=t.ToString();
// if(ts.ToUpper().LastIndexOf('E')>0)
// {
// ;
// }
// }
break;
}
// #endregion
}
else if (c == '!')//单目取反.)
{
sk.push(-(Double.parseDouble(sk.pop().toString())));
}
}
if (sk.size() > 1)
return "运算没有完成.";
if (sk.size() == 0)
return "结果丢失..";
return sk.pop().toString();
}
/// <summary>
/// 优先级别测试函数.
/// </summary>
/// <param name="opr"></param>
/// <returns></returns>
private static int Power(char opr)
{
switch (opr)
{
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '%':
case '^':
case '!':
return 3;
default:
return 0;
}
}
private static String FormatSpace(String s)
{
StringBuilder ret = new StringBuilder();
for (int i = 0; i < s.length(); i++)
{
if (!(s.length() > i + 1 && s.charAt(i) == ' ' && s.charAt(i+1) == ' '))
ret.append(s.charAt(i));
else
ret.append(s.charAt(i));
}
return ret.toString();//.Replace('!','-');
}
}
TO Inhibitory() :你说这个大家都知道,要么我们转后缀表达式计算,要么双栈直接计算,现在的问题是怎么样来分解操作符和操作数。
TO zsq007zsq007() :你的做法没有问题,一位一位的按照char作,然后将操作数组合,由string转换成double,不错。只是感觉比较繁琐。
我认为这个问题在java中应该有更加简洁的解决方案,所以才提出这个问题的。
根据find输出的group自己判断一下,+-*/()比较容易判断,其他的就当作数字
String s = "-30+3*((4+-2.3)+-3)";
String regex = "(?<=^)[\\+\\-]?\\d+(\\.\\d+)?|((?<=[+\\-\\*/])[+\\-\\*/]?\\d+(.\\d+)?)|[+\\-\\*/]|((?<=[\\(\\)])\\d+(\\.\\d+)?)|[\\(\\)]";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(s);
while(m.find()){
System.out.println(m.group());
}
大哥,不会你对java的类库熟悉的差不多了吧.....Matcher,Pattern这些东西见都没见过....汗一个
不过程序好象正确的,就是不太明白原理...
采用切分流就OK了!
StreamTokenizer类里这些功能都实现很大块了!
程序好长 都看不懂
(?=X) (?!X) (?<=X) (?<!X) (?>X)是什么意思?
java的api中的解释看不明白
楼主看了吗?分割字符串只一句正则:
String[] words = str.split("(?<!^-?|[+/*()-]-)((?<=[+/*()-])|(?=[+/*()-]))");
然后遍历words
if (words[i].matches("-?\\d+|-?\\d+\\.\\d+")) { //操作数
...
}
else { //操作符
...
}
SKIP :
{
" "
| "\t"
| "\n"
| "\r"
| <"//" (~["\n","\r"])* ("\n"|"\r"|"\r\n")>
| <"/*" (~["*"])* "*" (~["/"] (~["*"])* "*")* "/">
}TOKEN : /* LITERALS */
{
< INTEGER_LITERAL:
<DECIMAL_LITERAL> (["l","L"])?
| <HEX_LITERAL> (["l","L"])?
| <OCTAL_LITERAL> (["l","L"])?
>
|
< #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* >
|
< #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
|
< #OCTAL_LITERAL: "0" (["0"-"7"])* >
}TOKEN : /* IDENTIFIERS */
{
< IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* >
|
< #LETTER: ["_","a"-"z","A"-"Z"] >
|
< #DIGIT: ["0"-"9"] >
}SimpleNode Start() : {}
{
Expression() ";"
{ return jjtThis; }
}
void Expression() : {}
{
AdditiveExpression()
}void AdditiveExpression() : {}
{
MultiplicativeExpression() ( ( "+" | "-" ) MultiplicativeExpression() )*
}void MultiplicativeExpression() : {}
{
UnaryExpression() ( ( "*" | "/" | "%" ) UnaryExpression() )*
}void UnaryExpression() : {}
{
"(" Expression() ")" | Identifier() | Integer()
}void Identifier() : {}
{
<IDENTIFIER>
}void Integer() : {}
{
<INTEGER_LITERAL>
}