regex 通过应用正则表达式查找元素的内部到外部匹配

q7solyqu  于 2022-11-18  发布在  其他
关注(0)|答案(2)|浏览(107)

我正在尝试为字符串实现一个替换机制,比如在javascript中动态求值的预处理语句。

[{username:"Max",age:10}]

例如,假设我们有字符串作为输入(username) is (age),那么通过属性和它的值查找替换是很容易的。
然而,我想更先进的括号是'确定'和评估从内部到外部例如输入:

[{username:"Max",age:10,myDynamicAttribute:"1",label1:'awesome', label2:'ugly'}]

和字符串(username) is (age) and (label(myDynamicAttribute))。在第一次替换迭代中,字符串应该变成(username) is (age) and (label1),而在第二次替换迭代中,字符串应该变成Peter is 10 and awesome。有没有什么工具或模式可以让我先“理解”内括号,然后再计算其他括号?我尝试过正则表达式,但我无法创建一个先匹配内括号,然后再匹配外括号的正则表达式。

a9wyjsp7

a9wyjsp71#

我们可以写a regular expression来找到一个带括号的表达式,它不包含内部括号,使用表达式的内部值作为数据对象的键,用该值替换整个表达式,然后重复执行。当字符串不包含这样的带括号的表达式时,我们将停止,并完整地返回字符串。
这里有一种方法:

const interpolate = (data, str, parens = str .match (/\(([^\(\)]+)\)/)) =>
  parens ? interpolate (data, str. replace (parens [0], data [parens [1]])) : str

const data = {username: 'Max', age: 10, myDynamicAttribute: '1', label1: 'awesome', label2: 'ugly'}
const str = `(username) is (age) and (label(myDynamicAttribute))`

console .log (interpolate (data, str))

这将导致使用以下字符串进行一系列递归调用:

"(username) is (age) and (label(myDynamicAttribute))",
"Max is (age) and (label(myDynamicAttribute))",
"Max is 10 and (label(myDynamicAttribute))",
"Max is 10 and (label1)",
"Max is 10 and awesome"
wfveoks0

wfveoks02#

您可以标记化字符串,并使用递归替换器一次遍历这些标记。如果括号内的文本与对象属性不匹配,它们将保持原样。当从对象检索的字符串中出现括号时,它们将被视为文本,并且不会尝试再次对它们执行查找。

function interpolate(encoded, lookup) {
    const tokens = encoded.matchAll(/[^()]+|./g);
    
    function* dfs(end="") {
        while (true) {
            const {value, done} = tokens.next();
            if (value == end || done) return;
            if (value != "(") yield value;
            else {
                const key = [...dfs(")")].join("");
                yield lookup[key] ?? `(${key})`;
            }
        }
    }
    return [...dfs()].join(""); 
}

// Example run
const lookup = {
    username: "Max",
    age: 10,
    myDynamicAttribute: "1",
    label1019: 'awesome(really)', // These parentheses are treated as literals
    really: "not",                // ...so that this will not be retrieved
    label2: 'ugly',
};
const str = "1) (username) is (age) (uh!) and (label(myDynamicAttribute)0(myDynamicAttribute)9)"

const res = interpolate(str, lookup);
console.log(res);

相关问题