Java中是否有eval()函数?

kar*_*_ms 140 java eval

我有一个像下面这样的字符串:

String str = "4*5";
Run Code Online (Sandbox Code Playgroud)

现在我必须20通过使用字符串来获得结果.

我知道在其他一些语言中,这个eval()功能会做到这一点.我怎么能用Java做到这一点?

Jef*_*rey 141

您可以使用ScriptEngine该类并将其评估为Javascript字符串.

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
Object result = engine.eval("4*5");
Run Code Online (Sandbox Code Playgroud)

可能有更好的方法,但这个方法有效.

  • 除了有点矫枉过正之外,使用JavaScript解释器可以打开代码注入.如果你没有严格控制表达式,有人可以发送`while(true){3 + 4;}`并挂起你的JVM. (78认同)
  • 加载一个完整的JavaScript解释器来做一些数学运算似乎是一个巨大的浪费.但是你是对的,它有效,特别是如果效率不是优先考虑的话. (27认同)
  • 它在编码时间方面是有效的.如果你一直使用它,你可以考虑将它保存在内存中,也许是在单个对象中. (2认同)

Ste*_*n C 44

没有标准的Java类或方法可以执行您想要的操作.您的选择包括:

  • 选择并使用一些第三方表达式评估库.例如JEL此处列出的六个库中的任何一个.

  • 使用方法将表达式包装在带有eval方法的类的Java源代码中,将其发送到Java编译器,然后加载生成的编译类.

  • 使用一些可以从Java调用的脚本语言作为表达式求值程序.可能性包括Javascript,BeanShell等.

  • 从头开始编写自己的表达式求值程序.

第一种方法可能是最简单的.如果从不受信任的用户获得要评估的表达式,则第二种和第三种方法存在潜在的安全风险.(想想代码注入.)


Rae*_*ald 30

很少有真正的用例,其中能够将StringJava作为Java代码片段进行评估是必要或可取的.也就是说,询问如何做到这一点实际上是一个XY问题:你实际上有一个不同的问题,可以通过不同的方式解决.

首先问问自己,String您希望评估的内容来自哪里?您的程序的另一部分是否生成了它,或者是由用户提供的输入?

  • 我的程序的另一部分生成它:因此,您希望程序的一部分决定要执行的操作类型,但不执行操作,以及执行所选操作的第二部分.不是生成然后评估a String,而是使用策略,命令构建器设计模式,以适合您的特定情况.

  • 它是用户输入:用户可以输入任何内容,包括执行时可能导致程序出现异常,崩溃,泄露应该保密的信息,破坏持久性信息(如数据库内容)等命令的命令.污秽.防止这种情况的唯一方法是解析String自己,检查它是不是恶意的,然后评估它.但是自己解析它是所请求的eval函数所做的大部分工作,所以你没有为自己节省任何东西.更糟糕的是,检查任意Java不是恶意的是不可能的,因为检查这是暂停问题.

  • 它是用户输入,但是要评估的允许文本的语法和语义受到很大限制:没有通用工具可以轻松地为您选择的任何受限语法和语义实现通用解析器和求值器.您需要做的是为您选择的语法和语义实现解析器和求值程序.如果任务很简单,您可以手动编写一个简单的递归下降或有限状态机解析器.如果任务很困难,您可以使用编译器编译器(如ANTLR)为您完成一些工作.

  • 我只是想实现一个桌面计算器!:做作业,呃?如果你可以使用提供的eval函数来实现输入表达式的评估,那么它不会是一个很好的作业,是吗?你的程序将长三行.您的教师可能希望您为简单的算术解析器/评估器编写代码.有一个众所周知的算法,shunting-yard,你可能会发现它很有用.


chA*_*xey 12

我可以建议你使用Exp4j.从以下示例代码中可以看出,这很容易理解:

Expression e = new ExpressionBuilder("3 * sin(y) - 2 / (x - 2)")
    .variables("x", "y")
    .build()
    .setVariable("x", 2.3)
    .setVariable("y", 3.14);
double result = e.evaluate();
Run Code Online (Sandbox Code Playgroud)

  • 它看起来比JAVA世界中公认的答案更好。 (2认同)

sma*_*c89 10

使用Java 9,我们可以访问jshell,因此可以编写如下代码:

import jdk.jshell.JShell;
import java.lang.StringBuilder;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Eval {
    public static void main(String[] args) throws IOException {
        try(JShell js = JShell.create(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {

            js.onSnippetEvent(snip -> {
                if (snip.status() == jdk.jshell.Snippet.Status.VALID) {
                    System.out.println("? " + snip.value());
                }
            });

            System.out.print("> ");
            for (String line = br.readLine(); line != null; line = br.readLine()) {
                js.eval(js.sourceCodeAnalysis().analyzeCompletion(line).source());
                System.out.print("> ");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

样品运行:

> 1 + 2 / 4 * 3
? 1
> 32 * 121
? 3872
> 4 * 5
? 20
> 121 * 51
? 6171
>
Run Code Online (Sandbox Code Playgroud)

稍加操作,但这就是Java当前必须提供的功能

  • 如果我的公司允许我们使用 Java 9,这将是完美的 :) (2认同)

Adi*_*Adi 7

如前所述,Java中没有标准的API.

您可以在路径中添加groovy jar文件,groovy.util.Eval.me("4*5")可以完成您的工作.

  • 但是与使用JavaScript相同的代码注入问题. (4认同)
  • 检查答案的日期. (2认同)

DVK*_*DVK 5

,你不能在Java(或任何编译语言)中使用通用的"eval".除非您愿意编写Java编译器和JVM以在Java程序中执行.

是的,您可以使用一些库来评估上面的数字代数表达式 - 请参阅此主题进行讨论.

  • 嗯...[jshell](https://docs.oracle.com/javase/9​​/docs/api/jdk/jshell/package-summary.html) (2认同)

Vin*_*iuz 5

一种解决问题的有趣方法可能是自己编写 eval() 函数!我已经为你做了!

您只需在代码中键入FunctionSolver.solveByX( function , value )即可使用 FunctionSolver 库。该功能属性是代表你要解决功能的字符串,该属性是你的函数的自变量(必须是X)的值。

如果你想解决一个包含多个自变量的函数,你可以使用FunctionSolver.solve( function , values )其中values属性是一个HashMap(String,Double),它包含你所有的独立属性(作为字符串)及其各自的值(作为双打)。

另一条信息:我编写了一个简单版本的FunctionSolver,因此它仅支持返回 double 值并接受一个或两个 double 值作为字段的Math 方法如果您好奇,只需使用FunctionSolver.usableMathMethods()) (这些方法是:bs、sin、cos、tan、atan2、sqrt、log、log10、pow、exp、min、max、copySign、signum、IEEEremainder、acos、asin、atan、cbrt、ceil、cosh、expm1、floor , hypot, log1p, nextAfter, nextDown, nextUp, random, rint, sinh, tanh, toDegrees, toRadians, ulp)。此外,该库支持以下运算符: * / + - ^ (即使 java 通常不支持 ^ 运算符)。

最后一件事:在创建这个库时,我不得不使用反射来调用Math 方法。我觉得它真的很酷,有兴趣的可以看看这个

就是这样,这是代码(和):

package core;

 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;

 public abstract class FunctionSolver {

public static double solveNumericExpression (String expression) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    return solve(expression, new HashMap<>());
}

public static double solveByX (String function, double value) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

    HashMap<String, Double> values = new HashMap<>();
    values.put("x", value);
    return solveComplexFunction(function, function, values);
}

public static double solve (String function, HashMap<String,Double> values) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

    return solveComplexFunction(function, function, values);
}

private static double solveComplexFunction (String function, String motherFunction, HashMap<String, Double> values) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

    int position = 0;
    while(position < function.length()) {
        if (alphabetic.contains(""+function.charAt(position))) {
            if (position == 0 || !alphabetic.contains(""+function.charAt(position-1))) {
                int endIndex = -1;
                for (int j = position ; j < function.length()-1 ; j++) {
                    if (alphabetic.contains(""+function.charAt(j)) 
                            && !alphabetic.contains(""+function.charAt(j+1))) {
                        endIndex = j;
                        break;
                    }
                }
                if (endIndex == -1 & alphabetic.contains(""+function.charAt(function.length()-1))) {
                    endIndex = function.length()-1;
                }
                if (endIndex != -1) {
                    String alphabeticElement = function.substring(position, endIndex+1);
                    if (Arrays.asList(usableMathMethods()).contains(alphabeticElement)) {
                        //Start analyzing a Math function
                        int closeParenthesisIndex = -1;
                        int openedParenthesisquantity = 0;
                        int commaIndex = -1;
                        for (int j = endIndex+1 ; j < function.length() ; j++) {
                            if (function.substring(j,j+1).equals("(")) {
                                openedParenthesisquantity++;
                            }else if (function.substring(j,j+1).equals(")")) {
                                openedParenthesisquantity--;
                                if (openedParenthesisquantity == 0) {
                                    closeParenthesisIndex = j;
                                    break;
                                }
                            }else if (function.substring(j,j+1).equals(",") & openedParenthesisquantity == 0) {
                                if (commaIndex == -1) {
                                    commaIndex = j;
                                }else{
                                    throw new IllegalArgumentException("The argument of math function (which is "+alphabeticElement+") has too many commas");
                                }
                            }
                        }
                        if (closeParenthesisIndex == -1) {
                            throw new IllegalArgumentException("The argument of a Math function (which is "+alphabeticElement+") hasn't got the closing bracket )");
                        }   
                        String functionArgument = function.substring(endIndex+2,closeParenthesisIndex);
                        if (commaIndex != -1) {
                            double firstParameter = solveComplexFunction(functionArgument.substring(0,commaIndex),motherFunction,values);
                            double secondParameter = solveComplexFunction(functionArgument.substring(commaIndex+1),motherFunction,values);
                            Method mathMethod = Math.class.getDeclaredMethod(alphabeticElement, new Class<?>[] {double.class, double.class});
                            mathMethod.setAccessible(true);
                            String newKey = getNewKey(values);
                            values.put(newKey, (Double) mathMethod.invoke(null, firstParameter, secondParameter));
                            function = function.substring(0, position)+newKey
                                       +((closeParenthesisIndex == function.length()-1)?(""):(function.substring(closeParenthesisIndex+1)));
                        }else {
                            double firstParameter = solveComplexFunction(functionArgument, motherFunction, values);
                            Method mathMethod = Math.class.getDeclaredMethod(alphabeticElement, new Class<?>[] {double.class});
                            mathMethod.setAccessible(true);
                            String newKey = getNewKey(values);
                            values.put(newKey, (Double) mathMethod.invoke(null, firstParameter));
                            function = function.substring(0, position)+newKey
                                       +((closeParenthesisIndex == function.length()-1)?(""):(function.substring(closeParenthesisIndex+1)));
                        }   
                    }else if (!values.containsKey(alphabeticElement)) {
                        throw new IllegalArgumentException("Found a group of letters ("+alphabeticElement+") which is neither a variable nor a Math function: ");
                    }
                }
            }
        }
        position++;
    }
    return solveBracketsFunction(function,motherFunction,values);
}

private static double solveBracketsFunction (String function,String motherFunction,HashMap<String, Double> values) throws IllegalArgumentException{

    function = function.replace(" ", "");
    String openingBrackets = "([{";
    String closingBrackets = ")]}";
    int parenthesisIndex = 0;
    do {
        int position = 0;
        int openParenthesisBlockIndex = -1;
        String currentOpeningBracket = openingBrackets.charAt(parenthesisIndex)+"";
        String currentClosingBracket = closingBrackets.charAt(parenthesisIndex)+"";
        if (contOccouranceIn(currentOpeningBracket,function) != contOccouranceIn(currentClosingBracket,function)) {
            throw new IllegalArgumentException("Error: brackets are misused in the function "+function);
        }
        while (position < function.length()) {
            if (function.substring(position,position+1).equals(currentOpeningBracket)) {
                if (position != 0 && !operators.contains(function.substring(position-1,position))) {
                    throw new IllegalArgumentException("Error in function: there must be an operator following a "+currentClosingBracket+" breacket");
                }
                openParenthesisBlockIndex = position;
            }else if (function.substring(position,position+1).equals(currentClosingBracket)) {
                if (position != function.length()-1 && !operators.contains(function.substring(position+1,position+2))) {
                    throw new IllegalArgumentException("Error in function: there must be an operator before a "+currentClosingBracket+" breacket");
                }
                String newKey = getNewKey(values);
                values.put(newKey, solveBracketsFunction(function.substring(openParenthesisBlockIndex+1,position),motherFunction, values));
                function = function.substring(0,openParenthesisBlockIndex)+newKey
                           +((position == function.length()-1)?(""):(function.substring(position+1)));
                position = -1;
            }
            position++;
        }
        parenthesisIndex++;
    }while (parenthesisIndex < openingBrackets.length());
    return solveBasicFunction(function,motherFunction, values);
}

private static double solveBasicFunction (String function, String motherFunction, HashMap<String, Double> values) throws IllegalArgumentException{

    if (!firstContainsOnlySecond(function, alphanumeric+operators)) {
        throw new IllegalArgumentException("The function "+function+" is not a basic function");
    }
    if (function.contains("**") |
        function.contains("//") |
        function.contains("--") |
        function.contains("+*") |
        function.contains("+/") |
        function.contains("-*") |
        function.contains("-/")) {
        /*
         * ( -+ , +- , *- , *+ , /- , /+ )> Those values are admitted
         */
        throw new IllegalArgumentException("Operators are misused in the function");
    }
    function = function.replace(" ", "");
    int position;
    int operatorIndex = 0;
    String currentOperator;
    do {
        currentOperator = operators.substring(operatorIndex,operatorIndex+1);
        if (currentOperator.equals("*")) {
            currentOperator+="/";
            operatorIndex++;
        }else if (currentOperator.equals("+")) {
            currentOperator+="-";
            operatorIndex++;
        }
        operatorIndex++;
        position = 0;
        while (position < function.length()) {
            if ((position == 0 && !(""+function.charAt(position)).equals("-") && !(""+function.charAt(position)).equals("+") && operators.contains(""+function.charAt(position))) ||
                (position == function.length()-1 && operators.contains(""+function.charAt(position)))){
                throw new IllegalArgumentException("Operators are misused in the function");
            }
            if (currentOperator.contains(function.substring(position, position+1)) & position != 0) {
                int firstTermBeginIndex = position;
                while (firstTermBeginIndex > 0) {
                    if ((alphanumeric.contains(""+function.charAt(firstTermBeginIndex))) & (operators.contains(""+function.charAt(firstTermBeginIndex-1)))){
                        break;
                    }
                    firstTermBeginIndex--;
                }
                if (firstTermBeginIndex != 0 && (function.charAt(firstTermBeginIndex-1) == '-' | function.charAt(firstTermBeginIndex-1) == '+')) {
                    if (firstTermBeginIndex == 1) {
                        firstTermBeginIndex--;
                    }else if (operators.contains(""+(function.charAt(firstTermBeginIndex-2)))){
                        firstTermBeginIndex--;
                    }
                }
                String firstTerm = function.substring(firstTermBeginIndex,position);
                int secondTermLastIndex = position;
                while (secondTermLastIndex < function.length()-1) {
                    if ((alphanumeric.contains(""+function.charAt(secondTermLastIndex))) & (operators.contains(""+function.charAt(secondTermLastIndex+1)))) {
                        break;
                    }
                    secondTermLastIndex++;
                }
                String secondTerm = function.substring(position+1,secondTermLastIndex+1);
                double result;
                switch (function.substring(position,position+1)) {
                    case "*": result = solveSingleValue(firstTerm,values)*solveSingleValue(secondTerm,values); break;
                    case "/": result = solveSingleValue(firstTerm,values)/solveSingleValue(secondTerm,values); break;
                    case "+": result = solveSingleValue(firstTerm,values)+solveSingleValue(secondTerm,values); break;
                    case "-": result = solveSingleValue(firstTerm,values)-solveSingleValue(secondTerm,values); break;
                    case "^": result = Math.pow(solveSingleValue(firstTerm,values),solveSingleValue(secondTerm,values)); break;
                    default: throw new IllegalArgumentException("Unknown operator: "+currentOperator);
                }
                String newAttribute = getNewKey(values);
                values.put(newAttribute, result);
                function = function.substring(0,firstTermBeginIndex)+newAttribute+function.substring(secondTermLastIndex+1,function.length());
                deleteValueIfPossible(firstTerm, values, motherFunction);
                deleteValueIfPossible(secondTerm, values, motherFunction);
                position = -1;
            }
            position++;
        }
    }while (operatorIndex < operators.length());
    return solveSingleValue(function, values);
}

private static double solveSingleValue (String singleValue, HashMap<String, Double> values) throws IllegalArgumentException{

    if (isDouble(singleValue)) {
        return Double.parseDouble(singleValue);
    }else if (firstContainsOnlySecond(singleValue, alphabetic)){
        return getValueFromVariable(singleValue, values);
    }else if (firstContainsOnlySecond(singleValue, alphanumeric+"-+")) {
        String[] composition = splitByLettersAndNumbers(singleValue);
        if (composition.length != 2) {
            throw new IllegalArgumentException("Wrong expression: "+singleValue);
        }else {
            if (composition[0].equals("-")) {
                composition[0] = "-1";
            }else if (composition[1].equals("-")) {
                composition[1] = "-1";
            }else if (composition[0].equals("+")) {
                composition[0] = "+1";
            }else if (composition[1].equals("+")) {
                composition[1] = "+1";
            }
            if (isDouble(composition[0])) {
                return Double.parseDouble(composition[0])*getValueFromVariable(composition[1], values);
            }else if (isDouble(composition[1])){
                return Double.parseDouble(composition[1])*getValueFromVariable(composition[0], values);
            }else {
                throw new IllegalArgumentException("Wrong expression: "+singleValue);
            }
        }
    }else {
        throw new IllegalArgumentException("Wrong expression: "+singleValue);
    }
}

private static double getValueFromVariable (String variable, HashMap<String, Double> values) throws IllegalArgumentException{

    Double val = values.get(variable);
    if (val == null) {
        throw new IllegalArgumentException("Unknown variable: "+variable);
    }else {
        return val;
    }
}

/*
 * FunctionSolver help tools:
 * 
 */

private static final String alphabetic = "abcdefghilmnopqrstuvzwykxy";
private static final String numeric = "0123456789.";
private static final String alphanumeric = alphabetic+numeric;
private static final String operators = "^*/+-"; //--> Operators order in important!

private static boolean firstContainsOnlySecond(String firstString, String secondString) {

    for (int j = 0 ; j < firstString.length() ; j++) {
        if (!secondString.contains(firstString.substring(j, j+1))) {
            return false;
        }
    }
    return true;
}

private static String getNewKey (HashMap<String, Double> hashMap) {

    String alpha = "abcdefghilmnopqrstuvzyjkx";
    for (int j = 0 ; j < alpha.length() ; j++) {
        String k = alpha.substring(j,j+1);
        if (!hashMap.containsKey(k) & !Arrays.asList(usableMathMethods()).contains(k)) {
            return k;
        }
    }
    for (int j = 0 ; j < alpha.length() ; j++) {
        for (int i = 0 ; i < alpha.length() ; i++) {
            String k = alpha.substring(j,j+1)+alpha.substring(i,i+1);
            if (!hashMap.containsKey(k) & !Arrays.asList(usableMathMethods()).contains(k)) {
                return k;
            }
        }
    }
    throw new NullPointerException();
}

public static String[] usableMathMethods () {

    /*
     *  Only methods that:
     *  return a double type
     *  present one or two parameters (which are double type)
     */

    Method[] mathMethods = Math.class.getDeclaredMethods();
    ArrayList<String> usableMethodsNames = new ArrayList<>();
    for (Method method : mathMethods) {
        boolean usable = true;
        int argumentsCounter = 0;
        Class<?>[] methodParametersTypes = method.getParameterTypes();
        for (Class<?> parameter : methodParametersTypes) {
            if (!parameter.getSimpleName().equalsIgnoreCase("double")) {
                usable = false;
                break;
            }else {
                argumentsCounter++;
            }
        }
        if (!method.getReturnType().getSimpleName().toLowerCase().equals("double")) {
            usable = false;
        }
        if (usable & argumentsCounter<=2) {
            usableMethodsNames.add(method.getName());
        }
    }
    return usableMethodsNames.toArray(new String[usableMethodsNames.size()]);
}

private static boolean isDouble (String number) {
    try {
        Double.parseDouble(number);
        return true;
    }catch (Exception ex) {
        return false;
    }
}

private static String[] splitByLettersAndNumbers (String val) {
    if (!firstContainsOnlySecond(val, alphanumeric+"+-")) {
        throw new IllegalArgumentException("Wrong passed value: <<"+val+">>");
    }
    ArrayList<String> response = new ArrayList<>();
    String searchingFor;
    int lastIndex = 0;
    if (firstContainsOnlySecond(""+val.charAt(0), numeric+"+-")) {
        searchingFor = alphabetic;
    }else {
        searchingFor = numeric+"+-";
    }
    for (int j = 0 ; j < val.length() ; j++) {
        if (searchingFor.contains(val.charAt(j)+"")) {
            response.add(val.substring(lastIndex, j));
            lastIndex = j;
            if (searchingFor.equals(numeric+"+-")) {
                searchingFor = alphabetic;
            }else {
                searchingFor = numeric+"+-";
            }
        }
    }
    response.add(val.substring(lastIndex,val.length()));
    return response.toArray(new String[response.size()]);
}

private static void deleteValueIfPossible (String val, HashMap<String, Double> values, String function) {
    if (values.get(val) != null & funct