次のような文字列があります。
String str = "4*5";
次に、文字列を使用して20
の結果を取得する必要があります。
他のいくつかの言語では、eval()
関数がこれを行うことを知っています。 Javaでこれを行うにはどうすればよいですか?
ScriptEngine
クラスを使用して、Javascript文字列として評価できます。
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
Object result = engine.eval("4*5");
より良い方法があるかもしれませんが、これは機能します。
必要なことを行う標準のJavaクラスまたはメソッドはありません。次のオプションがあります。
サードパーティの式評価ライブラリを選択して使用します。たとえば、 JEL またはリストされている6種類のライブラリのいずれか here です。
クラスのJavaソースコードで式をeval
メソッドでラップし、それをJavaコンパイラーに送信し、結果のコンパイル済みクラスをロードします。
式エバリュエーターとしてJavaから呼び出すことができるスクリプト言語を使用します。可能性には、Javascript、BeanShellなどが含まれます。
独自の式エバリュエーターをゼロから作成します。
最初のアプローチはおそらく最も簡単です。信頼できないユーザーから式を評価する場合、2番目と3番目のアプローチは潜在的なセキュリティリスクです。 (コードインジェクションを考えてください。)
JavaコードのフラグメントとしてString
を評価できることが必要または望ましい実際のユースケースはほとんどありません。つまり、これを行う方法を尋ねることは、実際には XY問題 です。実際には、別の方法で解決できる別の問題があります。
まず、このString
を評価したいのはどこから来たのか自問してください。プログラムの別の部分で生成されたのですか、それともユーザーが入力したものですか?
プログラムの別の部分が生成した:したがって、プログラムの一部で実行する操作の種類を決定するが、操作は実行せず、2番目の部分で操作を実行する選択された操作。 String
を生成して評価する代わりに、特定のケースに応じて Strategy 、 Command または Builder のデザインパターンを使用します。 。
これはユーザー入力です。ユーザーは、実行時にプログラムの誤動作、クラッシュ、情報の公開を引き起こす可能性のあるコマンドを含むanythingを入力できますそれは秘密である必要があり、永続的な情報(データベースのコンテンツなど)、およびその他のそのような不快感を損ねます。それを防ぐ唯一の方法は、String
を自分で解析し、悪意がないことを確認してから評価することです。しかし、それを自分で解析することは、要求されたeval
functionが行う作業の多くであるため、何も保存していません。さらに悪いことに、任意のJava
が悪意のないものであるかどうかをチェックすることはimpossibleです。これは halting problem =。
これはユーザー入力ですが、評価できるテキストの構文とセマンティクスは大幅に制限されています:汎用構文やセマンティクスを制限する汎用パーサーとエバリュエーターを簡単に実装できる汎用機能はありません選択しました。行う必要があるのは、選択した構文とセマンティクスのパーサーとエバリュエーターを実装することです。タスクが単純な場合は、単純な recursive-descent または有限状態マシンパーサーを手で書くことができます。タスクが困難な場合は、 compiler-compiler (ANTLRなど)を使用して、いくつかの作業を行うことができます。
デスクトップ計算機を実装したいだけです!:宿題ですよね?提供されたeval
関数を使用して入力式の評価を実装できる場合、宿題の割り当てはそれほど多くないでしょうか?プログラムは3行の長さになります。インストラクターは、おそらく単純な算術パーサー/エバリュエーターのコードを書くことを期待しています。よく知られているアルゴリズム shunting-yard があります。これは役に立つかもしれません。
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();
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("> ");
}
}
}
}
サンプル実行:
> 1 + 2 / 4 * 3
➜ 1
> 32 * 121
➜ 3872
> 4 * 5
➜ 20
> 121 * 51
➜ 6171
>
わずかにOPですが、それがJavaが現在提供しているものです
以前の回答として、このためのJavaには標準APIはありません。
Groovy jarファイルをパスに追加すると、groovy.util.Eval.me( "4 * 5")がジョブを完了します。
No、Java(またはコンパイルされた言語)に一般的な「評価」を含めることはできません。 Javaコンパイラと実行するJVMを記述しない場合Javaプログラムの内部。
はい、上記のような数値代数式を評価するライブラリを用意できます- 議論についてはこのスレッドを参照してください 。
問題を解決する楽しい方法は、eval()関数を自分でコーディングすることです。私はあなたのためにそれをやった!
FunctionSolver.solveByX(function、value)コード内。 function属性は、解決したい関数を表す文字列です。value属性は、の独立変数の値ですあなたの関数(xでなければなりません)。
複数の独立変数を含む関数を解決する場合は、FunctionSolver.solve(function、values)values属性は HashMap(String、Double) whichすべての独立した属性(文字列として)とそれぞれの値(Doubleとして)が含まれます。
別の情報:FunctionSolverの単純なバージョンをコーディングしたので、 Mathメソッド のみをサポートし、doubleを返します値として、1つまたは2つのdouble値をフィールドとして受け入れます(興味がある場合は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が通常^演算子をサポートしていない場合でも)。
最後に、このライブラリを作成する際に reflections を使用して Mathメソッド を呼び出す必要がありました。私はそれが本当にクールだと思う、ただ これを見てください 興味があるなら!
それだけです、ここではコードです(そして library ):
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 & function != null) {
if (!function.contains(val)) {
values.remove(val);
}
}
}
private static int contOccouranceIn (String howManyOfThatString, String inThatString) {
return inThatString.length() - inThatString.replace(howManyOfThatString, "").length();
}
}
あなた自身のライブラリを書くことは、あなたが思うほど難しいことではありません。 Shunting-yard algorithm のステップバイステップアルゴリズム探索のリンクです。ただし、最初にトークンの入力を解析する必要があります。
いくつかの情報も提供できる他の2つの質問があります。 文字列を数式に変換しますか?javaで数式を解析するのに適したライブラリは何ですか?
多くの答えがあるので、要因のサポート、複雑な式の評価などの追加機能を備えたeval()
メソッドの上に実装を追加しています。
package evaluation;
import Java.math.BigInteger;
import Java.util.EmptyStackException;
import Java.util.Scanner;
import Java.util.Stack;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class EvalPlus {
private static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("This Evaluation is based on BODMAS rule\n");
evaluate();
}
private static void evaluate() {
StringBuilder finalStr = new StringBuilder();
System.out.println("Enter an expression to evaluate:");
String expr = scanner.nextLine();
if(isProperExpression(expr)) {
expr = replaceBefore(expr);
char[] temp = expr.toCharArray();
String operators = "(+-*/%)";
for(int i = 0; i < temp.length; i++) {
if((i == 0 && temp[i] != '*') || (i == temp.length-1 && temp[i] != '*' && temp[i] != '!')) {
finalStr.append(temp[i]);
} else if((i > 0 && i < temp.length -1) || (i==temp.length-1 && temp[i] == '!')) {
if(temp[i] == '!') {
StringBuilder str = new StringBuilder();
for(int k = i-1; k >= 0; k--) {
if(Character.isDigit(temp[k])) {
str.insert(0, temp[k] );
} else {
break;
}
}
Long prev = Long.valueOf(str.toString());
BigInteger val = new BigInteger("1");
for(Long j = prev; j > 1; j--) {
val = val.multiply(BigInteger.valueOf(j));
}
finalStr.setLength(finalStr.length() - str.length());
finalStr.append("(" + val + ")");
if(temp.length > i+1) {
char next = temp[i+1];
if(operators.indexOf(next) == -1) {
finalStr.append("*");
}
}
} else {
finalStr.append(temp[i]);
}
}
}
expr = finalStr.toString();
if(expr != null && !expr.isEmpty()) {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
try {
System.out.println("Result: " + engine.eval(expr));
evaluate();
} catch (ScriptException e) {
System.out.println(e.getMessage());
}
} else {
System.out.println("Please give an expression");
evaluate();
}
} else {
System.out.println("Not a valid expression");
evaluate();
}
}
private static String replaceBefore(String expr) {
expr = expr.replace("(", "*(");
expr = expr.replace("+*", "+").replace("-*", "-").replace("**", "*").replace("/*", "/").replace("%*", "%");
return expr;
}
private static boolean isProperExpression(String expr) {
expr = expr.replaceAll("[^()]", "");
char[] arr = expr.toCharArray();
Stack<Character> stack = new Stack<Character>();
int i =0;
while(i < arr.length) {
try {
if(arr[i] == '(') {
stack.Push(arr[i]);
} else {
stack.pop();
}
} catch (EmptyStackException e) {
stack.Push(arr[i]);
}
i++;
}
return stack.isEmpty();
}
}
更新されたGistはいつでも見つけてください here 。問題があればコメントもしてください。ありがとう。
JavaSEでこれを行うことはありません。サードパーティのライブラリを見つけるか、独自のライブラリを作成する必要があります。