作为我的问题Tokenizing an infix string in Java的答案,我得到了正则表达式(?<=[^\.a-zA-Z\d])|(?=[^\.a-zA-Z\d]
。但是,现在我在Javascript中编写相同的代码,而且我不知道如何让Javascript正则表达式做同样的事情。
例如,如果我有字符串sin(4+3)*2
,我需要将其解析为["sin","(","4","+","3",")","*","2"]
我将使用什么正则表达式将字符串标记为每个单独的部分。
之前,我做的是我只是对每个可能的令牌进行了字符串替换,并在其周围放置一个空格,然后拆分该空白。但是,该代码很快变得非常臃肿。
我需要拆分的运算符是标准数学运算符(+,-,*,/,^)
,以及函数名(sin,cos,tan,abs,etc...)
和逗号
这是一种快速,有效的方法吗?
答案 0 :(得分:2)
我不知道这是否会完成你想要实现的所有,但它对我有用:
'sin(4+3)*2'.match(/\d+\.?\d*|[a-zA-Z]+|\S/g);
// ["sin", "(", "4", "+", "3", ")", "*", "2"]
您可以将[a-zA-Z]+
部分替换为sin|cos|tan|etc
以仅支持数学函数。
答案 1 :(得分:2)
您可以利用正则表达式分组来执行此操作。您需要一个结合了不同可能令牌的正则表达式,并重复应用它。
我喜欢把不同的部分分开;它使维护和扩展更容易:
var tokens = [
"sin",
"cos",
"tan",
"\\(",
"\\)",
"\\+",
"-",
"\\*",
"/",
"\\d+(?:\\.\\d*)?"
];
你将所有这些粘合成一个大的正则表达式,每个标记之间都有|
:
var rtok = new RegExp( "\\s*(?:(" + tokens.join(")|(") + "))\\s*", "g" );
然后,您可以使用源字符串上的正则表达式操作进行标记:
function tokenize( expression ) {
var toks = [], p;
rtok.lastIndex = p = 0; // reset the regex
while (rtok.lastIndex < expression.length) {
var match = rtok.exec(expression);
// Make sure we found a token, and that we found
// one without skipping garbage
if (!match || rtok.lastIndex - match[0].length !== p)
throw "Oops - syntax error";
// Figure out which token we matched by finding the non-null group
for (var i = 1; i < match.length; ++i) {
if (match[i]) {
toks.push({
type: i,
txt: match[i]
});
// remember the new position in the string
p = rtok.lastIndex;
break;
}
}
}
return toks;
}
只是将令牌正则表达式与字符串重复匹配。正则表达式是使用“g”标志创建的,因此正则表达式机制将自动跟踪我们在每次匹配后开始匹配的位置。当它没有看到匹配时,或者当它发生但却必须跳过无效的东西才能找到它时,我们知道存在语法错误。当它匹配时,它在令牌数组中记录它匹配的令牌(非空组的索引)和匹配的文本。通过记住匹配的令牌索引,您可以省去在标记后必须弄清楚每个令牌字符串的含义的麻烦;你只需做一个简单的数字比较。
因此,调用tokenize( "sin(4+3) * cos(25 / 3)" )
会返回:
[ { type: 1, txt: 'sin' },
{ type: 4, txt: '(' },
{ type: 10, txt: '4' },
{ type: 6, txt: '+' },
{ type: 10, txt: '3' },
{ type: 5, txt: ')' },
{ type: 8, txt: '*' },
{ type: 2, txt: 'cos' },
{ type: 4, txt: '(' },
{ type: 10, txt: '25' },
{ type: 9, txt: '/' },
{ type: 10, txt: '3' },
{ type: 5, txt: ')' } ]
令牌类型1是sin
函数,类型4是左paren,类型10是数字等等。
编辑 - 如果你想匹配像“x”和“y”这样的标识符,那么我可能会使用一组不同的令牌模式,其中一个只是为了匹配任何标识符。这意味着解析器不会直接从词法分析器中找到关于“sin”和“cos”等的内容,但这没关系。这是令牌模式的替代列表:
var tokens = [
"[A-Za-z_][A-Za-z_\d]*",
"\\(",
"\\)",
"\\+",
"-",
"\\*",
"/",
"\\d+(?:\\.\\d*)?"
];
现在任何标识符都是类型1标记。
答案 2 :(得分:1)
提供一些可能性:
[a-zA-Z]+|\d+(?:\.\d+)?|.