JavaScript正则表达式:获取括号内的特定部分,而不包括额外的括号。

huangapple go评论70阅读模式
英文:

Javascript Regex: get specific parts inside brackets without taking extra brackets

问题

I am building a program that would simplify coding discord bots. 我正在开发一个程序,可以简化编写Discord机器人的过程。

I wanted to add inline functions, but seems like my regex selector is not the perfect match. 我想添加内联函数,但似乎我的正则选择器不够完美。

My program loops through every inline function and tests if it exists in the string. And replaces the inline function strings with the inline function returnings. 我的程序循环遍历每个内联函数,并检查它是否存在于字符串中。然后用内联函数的返回值替换内联函数字符串。

For example, {ping} is translated to 91. Or, {id author} is translated to id of the message author. 例如,{ping}被翻译为91。或者,{id author}被翻译为消息作者的id。

The problem is that if there are inline functions in an inline function. I managed to solve 1 inline function per 1, but as the number grow, my regex selector fails and just matches the wrong stuff. 问题是,如果内联函数中包含内联函数。我成功解决了每一个内联函数,但随着数量的增加,我的正则选择器失败了,只匹配到了错误的内容。

what i want it to match: 我希望它匹配的内容是:

{avatarurl {id {message}}} {avatarurl {id {message}}}

this is the pattern that didnt work out: new RegExp(`\{${func.name}([^\}]+[^])*\}`) 这是没有成功的模式:new RegExp(`\{${func.name}([^\}]+[^])*\}`)

the func.name is the function name. func.name是函数名。

i also need the part that comes after " {funcname " and before " }". 我还需要在"{funcname"和"}"之间的部分。

英文:

I am building a program that would simplify coding discord bots. I wanted to add inline functions, but seems like my regex selector is not the perfect match.

My program loops through every inline function and tests if it exists in the string. And replaces the inline function strings with the inline function returnings.

For example, {ping} is translated to 91. Or, {id author} is translated to id of the message author.

The problem is that if there are inline functions in an inline function. I managed to solve 1 inline function per 1, but as the number grow, my regex selector fails and just matches the wrong stuff.

what i want it to match:

{avatarurl {id {message}}}
           ^^^^^^^^^^^^^^

can even match this too:

{avatarurl {id {message}}}
               ^^^^^^^^^

this too:

{avatarurl {id {message}}}
^^^^^^^^^^^^^^^^^^^^^^^^^^

and so on.

it just needs to starts from {(function name) and end with }.

my regex selector fails at matching this part, it just matches extra brackets. I need a perfect regex selector that will match {blabla}, but still work when placed into another {blabla}.

this is the pattern that didnt work out: new RegExp(`\{${func.name}([^\}]+[^])*\}`)

the func.name is the function name.
i also need the part that comes after "{funcname" and before "}".

答案1

得分: 2

这是一个通用的非正则解决方案,用于处理括号(支持不平衡的输入)。

然后,您只需遍历树并搜索您的匹配项。

编辑:转换为类

class BracketTree {

    constructor (brackets, string) {

        if (typeof brackets != 'string' || brackets.length != 2 || brackets[0] == brackets[1]) {
            return null;
        }

        let opening = brackets[0];
        let closing = brackets[1];

        function parse (start) {

            let children = [];
            let pos = start;

            loop: while (pos < string.length) {

                switch (string[pos]) {

                case opening:
                    let child = parse(pos + 1);
                    children.push(child);
                    if (child.end == string.length) {
                        break loop;
                    }
                    pos = child.end;
                    break;

                case closing:
                    if (start == 0) {
                        children = [{
                            children, start, end: pos, opened: false, closed: true,
                            contents: string.slice(0, pos)
                        }];
                    }
                    else {
                        return {
                            children, start, end: pos, opened: true, closed: true,
                            contents: string.slice(start, pos)
                        };
                    }
                }

                pos++;
            }

            return (start == 0)? {
                children, start, end: string.length, opened: false, closed: false,
                contents: string
            }: {
                children, start, end: string.length, opened: true, closed: false,
                contents: string.slice(start)
            };
        }

        this.root = parse(0);
    }

    traverse (callback) {

        if (typeof callback != 'function') {
            return false;
        }

        let root = this.root;
        let input = root.contents;
        let nodeId = 0;

        function recurse (parent, level) {

            function callbackLeaf (start, end) {
                callback({
                    root, parent, level,
                    nodeId: nodeId++, childId: childId++,
                    start, end, contents: input.slice(start, end)
                });
            }

            function callbackBranch (branch) {
                return callback({
                    root, parent, branch, level,
                    nodeId: nodeId++, childId: childId++
                });
            }

            let children = parent.children;
            let childId = 0;
            if (children.length == 0) {
                callbackLeaf(parent.start, parent.end);
                return;
            }

            callbackLeaf(parent.start, children[0].start - children[0].opened);
            if (callbackBranch(children[0])) {
                recurse(children[0], level+1);
            }

            for (var i = 0; i < children.length-1; i++) {
                callbackLeaf(children[i].end + children[i].closed, children[i+1].start - children[i+1].opened);
                if (callbackBranch(children[i+1])) {
                    recurse(children[i+1], level+1);
                }
            }

            callbackLeaf(children[i].end + children[i].closed, parent.end);
        }

        recurse(root, 0);
        return true;
    }
}

let input = 'NOT OPENED {3}2}1}***{avatarurl {id {message}}} blah blah blah {1{2{3} NOT CLOSED';
let tree = new BracketTree('{}', input);

function filteredTraverse (caption, leafFilter, branchFilter) {
    console.log(`${'-'.repeat(29 - caption.length/2)} ${caption} `.padEnd(60, '-'));
    leafFilter ??= () => true;
    branchFilter ??= () => true;
    tree.traverse((args) => {
        if (args.branch) {
            return branchFilter(args);
        }
        if (leafFilter(args)) {
            console.log(`${'  '.repeat(args.level)}<${args.contents}>`);
        }
    });
}

filteredTraverse(
    'Ignore unbalanced and all their descendants',
    null,
    ({branch}) => branch.opened && branch.closed
);

filteredTraverse(
    'Ignore unbalanced but include their descendants',
    ({parent}) => parent.opened == parent.closed
);

filteredTraverse(
    'Ignore empty',
    ({start, end}) => start != end
);

filteredTraverse(
    'Show non-empty first children only',
    ({childId, start, end}) => childId == 0 && start != end
);

希望这对您有帮助!

英文:

This is a general non-regex solution to brackets (supports unbalanced input).

You can then just traverse the tree and search for your matches.

Edit: converted into a class

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

class BracketTree {
constructor (brackets, string) {
if (typeof brackets != &#39;string&#39; || brackets.length != 2 || brackets[0] == brackets[1]) {
return null;
}
let opening = brackets[0];
let closing = brackets[1];
function parse (start) {
let children = [];
let pos = start;
loop: while (pos &lt; string.length) {
switch (string[pos]) {
case opening:
let child = parse(pos + 1);
children.push(child);
if (child.end == string.length) {
break loop;
}
pos = child.end;
break;
case closing:
if (start == 0) {
children = [{
children, start, end: pos, opened: false, closed: true,
contents: string.slice(0, pos)
}];
}
else {
return {
children, start, end: pos, opened: true, closed: true,
contents: string.slice(start, pos)
};
}
}
pos++;
}
return (start == 0)? {
children, start, end: string.length, opened: false, closed: false,
contents: string
}: {
children, start, end: string.length, opened: true, closed: false,
contents: string.slice(start)
};
}
this.root = parse(0);
}
traverse (callback) {
if (typeof callback != &#39;function&#39;) {
return false;
}
let root = this.root;
let input = root.contents;
let nodeId = 0;
function recurse (parent, level) {
function callbackLeaf (start, end) {
callback({
root, parent, level,
nodeId: nodeId++, childId: childId++,
start, end, contents: input.slice(start, end)
});
}
function callbackBranch (branch) {
return callback({
root, parent, branch, level,
nodeId: nodeId++, childId: childId++
});
}
let children = parent.children;
let childId = 0;
if (children.length == 0) {
callbackLeaf(parent.start, parent.end);
return;
}
callbackLeaf(parent.start, children[0].start - children[0].opened);
if (callbackBranch(children[0])) {
recurse(children[0], level+1);
}
for (var i = 0; i &lt; children.length-1; i++) {
callbackLeaf(children[i].end + children[i].closed, children[i+1].start - children[i+1].opened);
if (callbackBranch(children[i+1])) {
recurse(children[i+1], level+1);
}
}
callbackLeaf(children[i].end + children[i].closed, parent.end);
}
recurse(root, 0);
return true;
}
}
let input = &#39;NOT OPENED {3}2}1}***{avatarurl {id {message}}} blah blah blah {1{2{3} NOT CLOSED&#39;;
let tree = new BracketTree(&#39;{}&#39;, input);
function filteredTraverse (caption, leafFilter, branchFilter) {
console.log(`${&#39;-&#39;.repeat(29 - caption.length/2)} ${caption} `.padEnd(60, &#39;-&#39;));
leafFilter ??= () =&gt; true;
branchFilter ??= () =&gt; true;
tree.traverse((args) =&gt; {
if (args.branch) {
return branchFilter(args);
}
if (leafFilter(args)) {
console.log(`${&#39;  &#39;.repeat(args.level)}&lt;${args.contents}&gt;`);
}
});
}
filteredTraverse(
&#39;Ignore unbalanced and all their descendants&#39;,
null,
({branch}) =&gt; branch.opened &amp;&amp; branch.closed
);
filteredTraverse(
&#39;Ignore unbalanced but include their descendants&#39;,
({parent}) =&gt; parent.opened == parent.closed
);
filteredTraverse(
&#39;Ignore empty&#39;,
({start, end}) =&gt; start != end
);
filteredTraverse(
&#39;Show non-empty first children only&#39;,
({childId, start, end}) =&gt; childId == 0 &amp;&amp; start != end
);

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年5月18日 05:34:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76276351.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定