【发布时间】:2018-01-08 10:03:42
【问题描述】:
在 Perl 中,当想要对字符串进行连续解析时,可以这样做 我的 $string = " a 1 # ";
while () {
if ( $string =~ /\G\s+/gc ) {
print "whitespace\n";
}
elsif ( $string =~ /\G[0-9]+/gim ) {
print "integer\n";
}
elsif ( $string =~ /\G\w+/gim ) {
print "word\n";
}
else {
print "done\n";
last;
}
}
来源:When is \G useful application in a regex?
它产生以下输出:
whitespace
word
whitespace
integer
whitespace
done
在 JavaScript(以及许多其他正则表达式风格)中,没有 \G 模式,也没有任何好的替代品。
所以我想出了一个非常简单的解决方案来满足我的目的。
<!-- language: lang-js -->
//*************************************************
// pattmatch - Makes the PAT pattern in ST from POS
// notice the "^" use to simulate "/G" directive
//*************************************************
function pattmatch(st,pat,pos)
{
var resu;
pat.lastIndex=0;
if (pos===0)
return pat.exec(st); // busca qualquer identificador
else {
resu = pat.exec(st.slice(pos)); // busca qualquer identificador
if (resu)
pat.lastIndex = pat.lastIndex + pos;
return resu;
} // if
}
所以,上面的例子在 JavaScript (node.js) 中看起来像这样:
<!-- language: lang-js -->
var string = " a 1 # ";
var pos=0, ret;
var getLexema = new RegExp("^(\\s+)|([0-9]+)|(\\w+)","gim");
while (pos<string.length && ( ret = pm(string,getLexema,pos)) ) {
if (ret[1]) console.log("whitespace");
if (ret[2]) console.log("integer");
if (ret[3]) console.log("word");
pos = getLexema.lastIndex;
} // While
console.log("done");
它产生与 Perl 代码 sn-p 相同的输出:
whitespace
word
whitespace
integer
whitespace
done
注意解析器在# 字符处停止。可以从pos位置继续解析另一个代码sn-p。
❖
JavaScript 中有没有更好的方法来模拟 Perl 的 /G 正则表达式模式?
后版
出于好奇,我决定将我的个人解决方案与@georg 提案进行比较。这里我没有说明哪个代码最好。对我来说,这是一个品味问题。
我的系统在很大程度上依赖于用户交互,它会变慢吗?
@ikegami 写了关于@georg 解决方案的文章:
...他的解决方案是减少您输入的次数 文件被复制...
所以我决定在重复代码 1000 万次的循环中比较这两种解决方案:
<!-- language: lang-js -->
var i;
var n1,n2;
var string,pos,m,conta,re;
// Mine code
conta=0;
n1 = Date.now();
for (i=0;i<10000000;i++) {
string = " a 1 # ";
pos=0, m;
re = new RegExp("^(\\s+)|([0-9]+)|(\\w+)","gim");
while (pos<string.length && ( m = pattMatch(string,re,pos)) ) {
if (m[1]) conta++;
if (m[2]) conta++;
if (m[3]) conta++;
pos = re.lastIndex;
} // While
}
n2 = Date.now();
console.log('Mine: ' , ((n2-n1)/1000).toFixed(2), ' segundos' );
// Other code
conta=0;
n1 = Date.now();
for (i=0;i<10000000;i++) {
string = " a 1 # ";
re = /^(?:(\s+)|([0-9]+)|(\w+))/i;
while (m = string.match(re)) {
if (m[1]) conta++;
if (m[2]) conta++;
if (m[3]) conta++;
string = string.slice(m[0].length)
}
}
n2 = Date.now();
console.log('Other: ' , ((n2-n1)/1000).toFixed(2) , ' segundos');
//*************************************************
// pattmatch - Makes the PAT pattern in ST from POS
// notice the "^" use to simulate "/G" directive
//*************************************************
function pattMatch(st,pat,pos)
{
var resu;
pat.lastIndex=0;
if (pos===0)
return pat.exec(st);
else {
resu = pat.exec(st.slice(pos));
if (resu)
pat.lastIndex = pat.lastIndex + pos;
return resu;
}
} // pattMatch
结果:
我的:11.90 秒
其他:10.77 秒
我的代码运行时间长了大约 10%。每次迭代花费大约 110 纳秒。
老实说,根据我个人的喜好,在一个用户交互繁重的系统中,我可以接受这种效率损失。
如果我的项目涉及使用多维数组或巨大的神经网络进行繁重的数学处理,我可能会重新考虑。
【问题讨论】:
-
在您的 Perl 代码中,您可能需要
/c标志而不是/i或/m。 -
谢谢,melpomene,我现在就修好
-
/gim应该是/gc。如果没有/c,匹配位置将在匹配失败时重置。 (/i和/m对这些模式毫无用处。)
标签: javascript regex perl parsing