Как можно токенизировать эту строку в Java? - PullRequest
3 голосов
/ 22 февраля 2010

Как я могу разбить эти простые математические выражения на отдельные строки?

Я знаю, что в основном хочу использовать регулярное выражение: "[0-9]+|[*+-^()]", но похоже, что String.split () не будет работать, поскольку он также использует токены-разделители.

Я хочу разделить все целые числа: 0-9 и все операторы * + - ^ ().

Итак, 578+223-5^2

Будет разбит на:

578  
+  
223  
-  
5  
^  
2  

Каков наилучший подход для этого?

Ответы [ 9 ]

3 голосов
/ 22 февраля 2010

Вы можете использовать StringTokenizer (String str, String delim, boolean returnDelims) , с операторами в качестве разделителей. Таким образом, по крайней мере, получите каждый токен отдельно (включая разделители). Затем вы можете определить, на какой токен вы смотрите.

3 голосов
/ 22 февраля 2010

Если пойти по этому пути в боковом направлении и предположить, что в конечном итоге вы намерены математически оценить строку, вам может быть лучше использовать ScriptEngine

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Evaluator {
private ScriptEngineManager sm = new ScriptEngineManager();
private ScriptEngine sEngine = sm.getEngineByName("js");

public double stringEval(String expr)
{
Object res = "";
        try {
           res = sEngine.eval(expr);
          }
         catch(ScriptException se) {
            se.printStackTrace();
        }
        return Double.parseDouble( res.toString());
}

}

Который вы можете затем назвать следующим образом:

Evaluator evr = new Evaluator();  
String sTest = "+1+9*(2 * 5)";  
double dd = evr.stringEval(sTest);  
System.out.println(dd); 

Я пошел по этому пути, работая над математической оценкой строк, и не столько операторы убьют вас в регулярных выражениях, сколько сложные вложенные выражения в скобках. Не изобретать велосипед - это: а) безопаснее, б) быстрее и в) означает менее сложный и вложенный код для обслуживания.

2 голосов
/ 22 февраля 2010

Это работает для образца строки, которую вы разместили:

String s = "578+223-5^2";
String[] tokens = s.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)");

Регулярное выражение полностью состоит из взглядов и взглядов ; он соответствует позиции (не символ, а «пробел» между символами), которой либо предшествует цифра, а затем не цифра, либо предшествует не цифра, а затем цифра.

Имейте в виду, что регулярные выражения плохо подходят для задачи анализа математических выражений. В частности, регулярные выражения не могут легко обрабатывать сбалансированные разделители, такие как скобки, особенно если они могут быть вложенными. (У некоторых разновидностей регулярных выражений есть расширения, облегчающие подобные вещи, но не Java).

Помимо этого, вы захотите обработать строку, используя более приземленные методы, такие как charAt() и substring() и Integer.parseInt(). Или, если это не учебное упражнение, используйте существующую библиотеку для анализа математических выражений.

РЕДАКТИРОВАТЬ: ... или eval() это как @Syzygy рекомендует .

1 голос
/ 22 февраля 2010

Вот короткая Java-программа, которая токенизирует такие строки. Если вы ищете оценку выражения, я могу (беззастенчиво) указать вам на этот пост: Решатель арифметических выражений в 64 строках

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

  public class Tokenizer {
     private String input;

     public Tokenizer(String input_) { input = input_.trim(); }

     private char peek(int i) {
        return i >= input.length() ? '\0' : input.charAt(i);
     }

     private String consume(String... arr) {
        for(String s : arr)
           if(input.startsWith(s))
              return consume(s.length());
        return null;
     }

     private String consume(int numChars) {
        String result = input.substring(0, numChars);
        input = input.substring(numChars).trim();
        return result;
     }

     private String literal() {
        for (int i = 0; true; ++i)
           if (!Character.isDigit(peek(i)))
              return consume(i);
     }

     public List<String> tokenize() {
        List<String> res = new ArrayList<String>();
        if(input.isEmpty())
           return res;

        while(true) {
           res.add(literal());
           if(input.isEmpty())
              return res;

           String s = consume("+", "-", "/", "*", "^");
           if(s == null)
              throw new RuntimeException("Syntax error " + input);
           res.add(s);
        }
     }

     public static void main(String[] args) {
        Tokenizer t = new Tokenizer("578+223-5^2");
        System.out.println(t.tokenize());
     }   
  }
1 голос
/ 22 февраля 2010

Для этого нельзя использовать String.split(), поскольку любые символы, соответствующие указанному шаблону, удаляются из вывода.

Если вы хотите использовать пробелы между токенами, вы можете сделать ...

"578 + 223 - 5 ^ 2 ".split(" ");

что дает ...

578
+
223
-
5
^
2
0 голосов
/ 22 февраля 2010

Вот мое решение токенизатора, которое допускает отрицательные числа (унарные).

Пока он делал все, что мне было нужно:

private static List<String> tokenize(String expression)
    {
        char c;
        List<String> tokens = new ArrayList<String>();
        String previousToken = null;
        int i = 0;
        while(i < expression.length())
        {
            c = expression.charAt(i);
            StringBuilder currentToken = new StringBuilder();

            if (c == ' ' || c == '\t') // Matched Whitespace - Skip Whitespace
            {
                i++;
            }
            else if (c == '-' && (previousToken == null || isOperator(previousToken)) && 
                    ((i+1) < expression.length() && Character.isDigit(expression.charAt((i+1))))) // Matched Negative Number - Add token to list
            {
                currentToken.append(expression.charAt(i));
                i++;
                while(i < expression.length() && Character.isDigit(expression.charAt(i)))
                {
                    currentToken.append(expression.charAt(i));
                    i++;
                }   
            }
            else if (Character.isDigit(c)) // Matched Number - Add to token list
            {
                while(i < expression.length() && Character.isDigit(expression.charAt(i)))
                {
                    currentToken.append(expression.charAt(i));
                    i++;
                }
            }
            else if (c == '+' || c == '*' || c == '/' || c == '^' || c == '-') // Matched Operator - Add to token list
            {
                currentToken.append(c);
                i++;
            }
            else // No Match - Invalid Token!
            {
                i++;
            }

            if (currentToken.length() > 0)
            {
                tokens.add(currentToken.toString());    
                previousToken = currentToken.toString();    
            }
        }   
        return tokens;
    }
0 голосов
/ 22 февраля 2010

Вы ставите только разделители в операторе split. Кроме того, - означает диапазон и должен быть экранирован.

"578+223-5^2".split("[*+\\-^()]")
0 голосов
/ 22 февраля 2010

Вы должны экранировать "()" в Java и '-'

myString.split("[0-9]+|[\\*\\+\\-^\\(\\)]");

0 голосов
/ 22 февраля 2010

Вам нужно сбежать от -. Я считаю, что кванторы (+ and *) теряют свое особое значение, как и скобки в классе символов. Если это не сработает, попробуйте убежать и от них.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...