匹配括号内的内容

时间:2014-01-25 03:07:54

标签: javascript pattern-matching

我的问题实际上非常简单:如何将字符串中外括号的内容与嵌套括号匹配,如下所示:

Lorem (ipsum (dolor) sit) amet

在很多情况下我一直在努力解决这个问题,在我看来应该有一个非常简单的解决方案,因为很多语言使用基于树的结构(HTML / XML使用<tag><tag></tag></tag>, JSON使用[[]]{"a":{}})。

这里的主要问题是使用任何正则表达式,匹配将在第一个右括号结束,而不是正确的结束。我还考虑过使用String.split('('),甚至创建一个带有开括号和右括号的索引的数组,但最终这些数字也让我无处可去(或许我忽略了这些策略可以使用的方式)。

到目前为止,我最好的尝试只是检查每一个字符,测试该字符是否为括号,并继续直到剩下0个已打开的括号:

var str = 'Lorem (ipsum (dolor) sit) amet';
var opened = 1;
var start = str.indexOf('(')+1
for (var i=start; i<str.length && opened; i++) {
    if (str.charAt(i) == '(') opened++;
    else if (str.charAt(i) == ')') opened--;
}
console.log(str.substring(start, i-1));

我真的很想知道,有没有更简单的方法可以做到这一点,也许使用我没想到的内置函数?在我看来应该有,但我自己无法找到任何或提出一个简单的解决方案。

3 个答案:

答案 0 :(得分:2)

潜在的问题是正则表达式无法描述需要匹配括号的语言,因为像这样的语言元素使语言上下文自由,这是一个经常被引用的下推自动机(PDA)功能增强的例子与有限状态自动机(FA)。

最基本的是,你想要的产品是

N = aNa

终端出现在非终端的两侧。这是无上下文语法的签名示例,需要PDA识别它。而不是更简单的语法。

N = aN

表示正则表达式,只需要FA识别它。

你需要一个更强大的识别类,然后正则表达式可以集合。幸运的是,有一个很好的javascript解析器生成器,我可以推荐pegjs

好的完成了,我说“一会儿吗?”我熟悉nodejs所以这是假设的。但是peg也支持浏览器中的操作,因为它只是javascript。

你知道,毕竟这次,我不太确定这是你想要的东西,但是我已经做到这一点,任何用至少一对括号包裹的东西都会被保留,否则就会被遗忘。我继续完成了它,即使它是因为一个大型项目用于说明目的。但如果那不是你想要的,那么他们看看生成的解析树,它就是JSON ......

我已将源存储在github GIST中,随时可以下载,或其他任何

pegjs source

driver javascript

generated parser source

Sample 1

Sample 2

cat input1
Lorem (ipsum (dolor) sit) amet
node match.js < input1
ipsum (dolor) sit 

cat input2
Lorem (ipsum (dolor) sit) amet (a) a b s  (asd f d a (a d d d a) asda )
node match.js < input2
ipsum (dolor) sit  a    asd f d a (a d d d a) asda

答案 1 :(得分:0)

如果您知道只有一对外括号,可以采用更快的方法:从左侧扫描字符串,直到您遇到左括号(strchr);从右侧扫描,直到您遇到右括号(strrchr)。

三个好处:1)在扫描期间,您只测试单个字符值; 2)你可以在括号内进行任何比较; 3)为此目的有内置。

如果可以有多个外部对,则无法避免扫描整个字符串并记录嵌套级别。

答案 2 :(得分:0)

根据当前的答案,以及代码的简单性缺乏改进,除了必须下载一个非常大的库,我将假设没有更简单的方法来做到这一点。这是一个易于使用的函数形式的代码:

function getMatchedBracket(str, pos) {
    pos = pos || 0;
    var opened = 1;
    var start = i = str.indexOf('(', pos)+1
    for (; i<str.length; i++) {
        if (str.charAt(i) === '(') ++opened;
        else if (str.charAt(i) === ')' && !--opened) break;
    }
    return str.substring(start, i);
}
//and to test it
console.log(getMatchedBracket('Lorem (ipsum (dolor) sit) amet')); //ipsum (dolor) sit
//If you find this doesn't work correctly for anything, please let me know.

这里的forloop不使用开关,因为those are slow in this case。此外,检查循环是否应该终止的测试不再适用。

相关问题