【问题标题】:Advanced tokenizer for a complex math expression用于复杂数学表达式的高级标记器
【发布时间】:2013-05-11 15:21:17
【问题描述】:

我想标记一个由整数、浮点数、运算符、函数、变量和括号组成的字符串。下面的例子应该可以阐明问题的本质:

当前状态:

String infix = 4*x+5.2024*(Log(x,y)^z)-300.12

想要的状态:

 String tokBuf[0]=4 
 String tokBuf[1]=* 
 String tokBuf[2]=x 
 String tokBuf[3]=+ 
 String tokBuf[4]=5.2024 
 String tokBuf[5]=* 
 String tokBuf[6]=( 
 String tokBuf[7]=Log
 String tokBuf[8]=( 
 String tokBuf[9]=x
 String tokBuf[10]=, 
 String tokBuf[11]=y 
 String tokBuf[12]=) 
 String tokBuf[13]=^ 
 String tokBuf[14]=z 
 String tokBuf[15]=) 
 String tokBuf[16]=- 
 String tokBuf[17]=300.12

我们将不胜感激所有提示和解决方案。

【问题讨论】:

  • 使用antlr 之类的东西来定义表达式的语法并为其生成解析器。
  • yacc 是另一个不错的工具。您很可能需要符号图而不是数组。
  • 如果您正在构建表达式评估器,请尝试使用 GNU libmatheval 或 muParse :)
  • Scala 内置了对您可以使用的Combinator Parsers 的支持。 Programming in Scala 的第 33 章有一个使用它们解析数学表达式的示例。您可以直接从 Java 中使用 Scala 编译的类。
  • 如果你和我一样不使用语言工具,我可以与你分享我的表达式解析器。

标签: java string tokenize


【解决方案1】:

使用 Java 流标记器。界面有点奇怪,但是习惯了:

http://docs.oracle.com/javase/7/docs/api/java/io/StreamTokenizer.html

解析为请求的字符串列表的示例代码(您可能希望直接使用标记器或至少使用一个对象列表,以便您可以将数字直接存储为 Double):

public static List<String> tokenize(String s) throws IOException {
  StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(s));
  tokenizer.ordinaryChar('-');  // Don't parse minus as part of numbers.
  tokenizer.ordinaryChar('/');  // Don't treat slash as a comment start.
  List<String> tokBuf = new ArrayList<String>();
  while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
    switch(tokenizer.ttype) {
      case StreamTokenizer.TT_NUMBER:
        tokBuf.add(String.valueOf(tokenizer.nval));
        break;
      case StreamTokenizer.TT_WORD:
        tokBuf.add(tokenizer.sval);
        break;
      default:  // operator
        tokBuf.add(String.valueOf((char) tokenizer.ttype));
    }
  }
  return tokBuf; 
}

试运行:

System.out.println(tokenize("4*x+5.2024*(Log(x,y)^z)-300.12"));
[4.0, *, x, +, 5.2024, *, (, Log, (, x, ,, y, ), ^, z, ), -, 300.12]

【讨论】:

  • 附注你可能对我在 github 上的表达式解析器感兴趣:github.com/stefanhaustein/expressionparser
  • 您需要添加 tokenizer.ordinaryChar('/') 因为 '/' 被视为 EOF,如果存在斜杠运算符,循环将提前终止。
  • 完成。识别 C++ cmets 会关闭它吗?改用 reset() 可能是最安全的,然后明确设置所有内容...
【解决方案2】:

http://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form
http://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools
算法示例:
步骤#1:读取 '4' => 数字标记 => 读取字符直到到达非数字符号(即 '*')。刚刚读取的第一个,tokBuf[0] 是一个数字标记。
step#2:read '*' => token 代表一个二元运算符。
step#3:读“x”。也许,ot 一个函数符号 => 将下一个标记标记为 var-token。
等等。
下一步是评估,我猜?反向波兰符号或语法树将有所帮助...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多