我一直在研究一个解析公式的函数已经有一段时间了,但是还没能使它正常工作。它似乎并不总是有效 - 它会过滤文本的某些部分,但不是全部。
parseFormula(e) {
var formula = e.value, value = 0.00, tValue = 0.00, tFormula = '', dObj = {};
if(formula !== undefined && formula !== "") {
dObj._formulaIn = formula;
var f = formula.split(/\s/g);
for(var i = 0; i < f.length; i++) {
tFormula = f[i];
// Replacing PI
tFormula = tFormula.replace(/(pi)/gi,Math.PI);
dObj._1_pi_done = tFormula;
// Replacing Squareroot with placeholder
tFormula = tFormula.replace(/(sqrt)/gi,"__sqrt__");
tFormula = tFormula.replace(/(sqr)/gi,"__sqrt__");
tFormula = tFormula.replace(/(kvrt)/gi,"__sqrt__");
tFormula = tFormula.replace(/(kvr)/gi,"__sqrt__");
dObj._2_sqrt_done = tFormula;
// Removing units that may cause trouble
tFormula = tFormula.replace(/(m2||m3||t2||t3||c2||c3)/gi,"");
dObj._3_units_done = tFormula;
// Removing text
tFormula = tFormula.replace(/\D+[^\*\/\+\-]+[^\,\.]/gi,"");
dObj._4_text_done = tFormula;
// Removing language specific decimals
if(Language.defaultLang === "no_NB") {
tFormula = tFormula.replace(/(\.)/gi,"");
tFormula = tFormula.replace(/(\,)/gi,".");
} else {
tFormula = tFormula.replace(/(\,)/gi,"");
}
dObj._5_lang_done = tFormula;
// Re-applying Squareroot
tFormula = tFormula.replace(/(__sqrt__)/g,"Math.sqrt");
dObj._6_sqrt_done = tFormula;
if(tFormula === "") {
f.splice(i,1);
} else {
f[i] = tFormula;
}
dObj._7_splice_done = tFormula;
console.log(dObj);
}
formula = "";
for(var j = 0; j < f.length; j++) {
formula += f[j];
}
try {
value = eval(formula);
}
catch(err) {}
return value === 0 ? 0 : value.toFixed(4);
} else {
return 0;
}
}
我不确定此函数中使用的任何RegEx,因此我寻求帮助的原因。例如,我不确定/(pi)/
是否是获取确切文本“pi”的正确方法,并将其替换为3.141。
(我现在正在使用eval
,但它仅用于开发)
任何帮助表示感谢。
修改
我尝试解析的公式是用户输入公式。他/她会输入类似的内容:2/0.6 pcs of foo * pi bar + sqrt(4) foobar
。我希望它去除所有非数学字母并计算其余部分。意味着上述公式将被解释为(2/0.6) * 3.141 + Math.sqrt(4)
=&gt; 12.47
编辑2:
e
是一个ExtJS对象,由网格中的字段传递,它包含以下变量:
colIdx
(int)column
(Ext.grid.column.Column)field
(字符串)grid
(Ext.grid.Panel)originalValue
(字符串)record
(Ext.data.Model)row
(css选择器)rowIdx
(int)store
(Ext.data.Store)value
(字符串)view
(Ext.grid.View)目前无法让JSFiddle正常工作。
答案 0 :(得分:6)
标记化要解析的表达式可能更容易。标记化时,更容易阅读该标记流并构建自己的表达式。
I've put up a demo on jsFiddle which can parse your given formula
在演示中,我使用此Tokenizer
类和标记从公式中构建TokenStream
。
function Tokenizer() {
this.tokens = {};
// The regular expression which matches a token per group.
this.regex = null;
// Holds the names of the tokens. Index matches group. See buildExpression()
this.tokenNames = [];
}
Tokenizer.prototype = {
addToken: function(name, expression) {
this.tokens[name] = expression;
},
tokenize: function(data) {
this.buildExpression(data);
var tokens = this.findTokens(data);
return new TokenStream(tokens);
},
buildExpression: function (data) {
var tokenRegex = [];
for (var tokenName in this.tokens) {
this.tokenNames.push(tokenName);
tokenRegex.push('('+this.tokens[tokenName]+')');
}
this.regex = new RegExp(tokenRegex.join('|'), 'g');
},
findTokens: function(data) {
var tokens = [];
var match;
while ((match = this.regex.exec(data)) !== null) {
if (match == undefined) {
continue;
}
for (var group = 1; group < match.length; group++) {
if (!match[group]) continue;
tokens.push({
name: this.tokenNames[group - 1],
data: match[group]
});
}
}
return tokens;
}
}
TokenStream = function (tokens) {
this.cursor = 0;
this.tokens = tokens;
}
TokenStream.prototype = {
next: function () {
return this.tokens[this.cursor++];
},
peek: function (direction) {
if (direction === undefined) {
direction = 0;
}
return this.tokens[this.cursor + direction];
}
}
定义代币
tokenizer.addToken('whitespace', '\\s+');
tokenizer.addToken('l_paren', '\\(');
tokenizer.addToken('r_paren', '\\)');
tokenizer.addToken('float', '[0-9]+\\.[0-9]+');
tokenizer.addToken('int', '[0-9]+');
tokenizer.addToken('div', '\\/');
tokenizer.addToken('mul', '\\*');
tokenizer.addToken('add', '\\+');
tokenizer.addToken('constant', 'pi|PI');
tokenizer.addToken('id', '[a-zA-Z_][a-zA-Z0-9_]*');
通过定义上述标记,标记生成器可以识别公式中的所有内容。当公式
2/0.6 pcs of foo * pi bar + sqrt(4) foobar
被标记化,结果将是类似于
的标记流int(2), div(/), float(0.6), whitespace( ), id(pcs), whitespace( ), id(of), whitespace( ), id(foo), whitespace( ), mul(*), whitespace( ), constant(pi), whitespace( ), id(bar), whitespace( ), add(+), whitespace( ), id(sqrt), l_paren((), int(4), r_paren()), whitespace( ), id(foobar)
答案 1 :(得分:0)
您无法真正使用正则表达式来匹配公式。公式是context-free language,正则表达式仅限于regular languages,后者是前者的子集。有许多算法可用于识别无上下文的语言,例如CYK和LL parsers。如果您已经没有,那么我不推荐研究那些,因为主题非常大。
尽管如此,您可以快速,高效和轻松地尝试使用Reverse Polish Notation (RPN)计算公式(使用Shunting Yard algorithm将公式转换为RPN)。如果尝试失败(由于括号没有加工,无效的函数/常数,w / e),显然文本不是公式,否则一切都很好。调车码并不是一个特别困难的算法,您应该毫不费力地实施它。即使你这样做,我上面链接的维基百科页面也有伪代码,SO中也有很多问题可以帮助你。