how to parse an input string such as “4>1”, resolv

2019-09-23 06:42发布

This question already has an answer here:

I am learning Java now and don't know how to convert an input string(such as "4>1") to get a boolean return.

The goal that I want to achieve is that, let Java return boolean(true/false) based on the user input, which is in String?

For example, I tried to get a return value of "true" for input "!(4>=10)".

2条回答
Luminary・发光体
2楼-- · 2019-09-23 07:32

There is no easy answer

For starters there are no easy solutions. I noticed somebody mentioning Boolean.valueOf(...);

The goal of the Boolean.valueOf(String) method is not to evaluate conditions or equations. It is just a simple method to convert a String with value "true" or "false" to a Boolean object.

Anyway, if you want this kind of functionality, you have to set some clear limitations. (note: some equations have no answer: "0/0 = 0/0")

Regex parsing

If you are simply comparing integers with integers, then you could assume that the equations will always be in the following format:

<integer1>[whitespace]<operator>[whitespace]<integer2>

Then, what you could do, is split your string in 3 parts using a regular expression.

public static boolean evaluate(String input)
{
  Pattern compile = Pattern.compile("(\\d+)\\s*([<>=]+)\\s*(\\d+)");
  Matcher matcher = compile.matcher(input);
  if (matcher.matches())
  {
    String leftPart = matcher.group(1);
    String operatorPart = matcher.group(2);
    String rightPart = matcher.group(3);

    int i1 = Integer.parseInt(leftPart);
    int i2 = Integer.parseInt(rightPart);

    if (operatorPart.equals("<")) return i1 < i2;
    if (operatorPart.equals(">")) return i1 > i2;
    if (operatorPart.equals("=")) return i1 == i2;
    if (operatorPart.equals("<=")) return i1 <= i2;
    if (operatorPart.equals(">=")) return i1 >= i2;

    throw new IllegalArgumentException("invalid operator '" + operatorPart + "'");
  }

  throw new IllegalArgumentException("invalid format");
}

Script Engines

Java also supports script engines (e.g. Nashorn and others). These engines can call javascript methods, such as the eval(...) javascript method, which is exactly what you need. So, this is probably a better solution.

public static boolean evaluate(String input)
{
  try
  {
    ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
    Object result = engine.eval("eval('" + input + "');");
    return Boolean.TRUE.equals(result);
  }
  catch (ScriptException e)
  {
    throw new IllegalArgumentException("invalid format");
  }
}

This solution can handle more complicated input, such as "!(4>=10)".

Note: for security reasons, you may want to strip specific characters from the user input. (e.g. the ' character)

查看更多
聊天终结者
3楼-- · 2019-09-23 07:40

Suye Shen, in Java strings are not expressions - they are just a series of characters, and if you ask what the boolean value of them they will always be false (to say, any string has no boolean value).

You can turn a string into an expression that can have a boolean value - you just need to create a parser to read the contents of the string - and a resolver, to evaluate the parsed symbols into a boolean value.

Making a parser and a resolver is not very hard, but it's worth noting that it can change dramatically if you add more math into the problem. The way you wrote your problem, you just want to evaluate expressions like:

4>10

4<=100

!10>=3

!28<5

So basically, you just want to support the symbols {!, <=, >=, <, >}. Not even "+", "-" or "=" are required. and you only want to use Integers (whole numbers) as the arguments. That makes it simple. I even removed the "(" and ")" to make it the simplest of examples.

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import org.apache.axis.utils.StringUtils;


class CheckUrlsSafeFromFile {

  private Scanner s;

  public static void main(String args[]) throws Exception {
    Scanner scanner = new Scanner(System.in);
    String token = scanner.next();

    //read the user input and resolve it to true/false, until user type 'exit'
    while (!token.equals("exit")) {
      System.out.println(resolveString(parseString(token)));
      token = scanner.next();
    }
    scanner.close();

  } 

  //this is the most basic parser. It takes in a string, and returns a 
  //list of numbers and symbols. 
  public static List<String> parseString (String input) {

    List<String> tokens = new ArrayList<String>();
    if (StringUtils.isEmpty(input)) return tokens;
    if (input.indexOf("!")==0) {
      tokens.add("not");
      input = input.substring(1);
      tokens.addAll(parseString(input));
    }
    else if (input.indexOf("<=")>0 && input.indexOf("<=")<input.length()-2) {
      String firstArgument = input.substring(0,input.indexOf("<="));
      String secondArgument = input.substring(input.indexOf("<=")+2);
      tokens.addAll(parseString(firstArgument));
      tokens.add("smaller-equals");
      tokens.addAll(parseString(secondArgument));
    }
    else if (input.indexOf(">=")>0 && input.indexOf(">=")<input.length()-2) {
      String firstArgument = input.substring(0,input.indexOf(">="));
      String secondArgument = input.substring(input.indexOf(">=")+2);
      tokens.addAll(parseString(firstArgument));
      tokens.add("greater-equals");
      tokens.addAll(parseString(secondArgument));
    }
    else if (input.indexOf("<")>0 && input.indexOf("<")<input.length()-1) {
      String firstArgument = input.substring(0,input.indexOf("<"));
      String secondArgument = input.substring(input.indexOf("<")+1);
      tokens.addAll(parseString(firstArgument));
      tokens.add("smaller");
      tokens.addAll(parseString(secondArgument));
    }
    else if (input.indexOf(">")>0 && input.indexOf(">")<input.length()-1) {
      String firstArgument = input.substring(0,input.indexOf(">"));
      String secondArgument = input.substring(input.indexOf(">")+1);
      tokens.addAll(parseString(firstArgument));
      tokens.add("greater");
      tokens.addAll(parseString(secondArgument));
    }
    else {
      tokens.add(input);
    }

    return tokens;
  }

  //And this is the resolver.
  public static boolean resolveString (List<String> parsedTokens) {
    if (parsedTokens==null || parsedTokens.size()==0) return false;
    if (parsedTokens.size()==1) return false;
    if (parsedTokens.get(0).equals("not")) {
      parsedTokens.remove(0);
      return !resolveString(parsedTokens);
    }
    if (parsedTokens.size()>2 && parsedTokens.get(1).equals("greater")) {
      String firstArgument = parsedTokens.get(0);
      String secondArgument = parsedTokens.get(2);
      int firstArgumentInt = Integer.parseInt(firstArgument);
      int secondArgumentInt = Integer.parseInt(secondArgument);
      return firstArgumentInt>secondArgumentInt;
    }
    if (parsedTokens.size()>2 && parsedTokens.get(1).equals("greater-equals")) {
      String firstArgument = parsedTokens.get(0);
      String secondArgument = parsedTokens.get(2);
      int firstArgumentInt = Integer.parseInt(firstArgument);
      int secondArgumentInt = Integer.parseInt(secondArgument);
      return firstArgumentInt>=secondArgumentInt;
    }
    if (parsedTokens.size()>2 && parsedTokens.get(1).equals("smaller")) {
      String firstArgument = parsedTokens.get(0);
      String secondArgument = parsedTokens.get(2);
      int firstArgumentInt = Integer.parseInt(firstArgument);
      int secondArgumentInt = Integer.parseInt(secondArgument);
      return firstArgumentInt<secondArgumentInt;
    }
    if (parsedTokens.size()>2 && parsedTokens.get(1).equals("smaller-equals")) {
      String firstArgument = parsedTokens.get(0);
      String secondArgument = parsedTokens.get(2);
      int firstArgumentInt = Integer.parseInt(firstArgument);
      int secondArgumentInt = Integer.parseInt(secondArgument);
      return firstArgumentInt<=secondArgumentInt;
    }
    return false;
  }
}

The result of running this may be like:

4>10
false
4<=100
true
!10>=3
false
!28<5
true
exit

I hope you can take this basic example and expand on it to include more math like {+,-,=,/,*,(,)} and more symbols, as well as non integer numbers. There are libraries for parsing and resolving out there that may be used, but tackling this issue on your own could be a good Java lesson.

查看更多
登录 后发表回答