用字符串中相应的值替换多个占位符

Replace multiple placeholders with their corresponding values in string

我有一个自定义欢迎消息的命令,所以当有人说将标题更改为自定义占位符时 {user.id} 它会将标题更改为加入用户 ID

我试过 ${(cardText, placeholders)} 但只有 returns [object Object] 嵌入

所以 cardText 是 {user.tag} 所以我希望它查看 placeholders 并查看字符串中是否有占位符以及是否将其更改为正确的值所以在这种情况下它会将其更改为 member.user.tag

代码:

module.exports = async (client, member) => {
    
    const guildSchema = require('../../models/guildSchema');
    const data = await guildSchema.findOne({
        id: member.guild.id
    });
 
    const placeholders = {
        '{client.avatar}': client.user.displayAvatarURL(),
        '{client.username}': client.user.username,
        '{client.mention}': client.user,
        '{client.tag}': client.user.tag,
        '{client.id}': client.user.id,
 
        '{user.avatar}': member.user.displayAvatarURL(),
        '{user.username}': member.user.username,
        '{user.mention}': member.user,
        '{user.tag}': member.user.tag,
        '{user.id}': member.user.id,
 
        '{guild.memberCount}': member.guild.memberCount,
        '{guild.icon}': member.guild.iconURL,
        '{guild.name}': member.guild.name,
        '{guild.id}': member.guild.id,
    };
    
    const cardTitle = (data.welcome.title === null) ? 'Welcome To The Server' : data.welcome.title;
    const cardText = (data.welcome.text === null) ? '{user.tag}' : data.welcome.text;
    const cardSubtitle = (data.welcome.subtitle === null) ? 'Member Count: {guild.memberCount}' : data.welcome.text;
 
        const embed = new Discord.MessageEmbed()
        .setTitle(`${(cardTitle, placeholders)}`),
        .setDescription(`${(cardText, placeholders)}`),
        .setFooter(`${(cardSubtitle, placeholders)}`),
 
        channel.send({ embeds: [embed] });
}

OP 代码的问题在于他们遵循的示例有一些约定,这些约定让用户在他们的消息文本中有模板,由 {} 表示。

例如,在 'Member Count: {guild.memberCount}' 中,{guild.memberCount} 将被替换为 'Member Count: 123' 或其他内容。

但替换此占位符的函数在其源代码中缺失。

因此,解析函数如:

const replacePlaceholder = (template, placeholders) => {
  //if there is no placeholder, return.
  if (template.indexOf('{') === -1) return template;
  //get the string inside the {}
  const toReplace = template.slice(
    template.indexOf('{'),
    template.indexOf('}') + 1
  );

  //keep replacing until { character is not found.
  return replacePlaceholder(
    template.replace(toReplace, placeholders[toReplace]),
    placeholders
  );
};

需要。在这种情况下,OP 使用的原因也是有道理的:

const embed = new Discord.MessageEmbed()
  .setTitle(`${(cardTitle, placeholders)}`),
  .setDescription(`${(cardText, placeholders)}`),
  .setFooter(`${(cardSubtitle, placeholders)}`),

它们最初是函数调用,例如:

const embed = new Discord.MessageEmbed()
  .setTitle(`${replacePlaceholder(cardTitle, placeholders)}`),
  .setDescription(`${replacePlaceholder(cardText, placeholders)}`),
  .setFooter(`${replacePlaceholder(cardSubtitle, placeholders)}`),

让我们使用正则表达式来为我们做匹配。因为您的键包含在正则表达式中具有特殊含义的字符({}),我们需要转义这些字符。

来自 escape-string-regexp:

const escapeRegex = (string) => string
    .replace(/[|\{}()[\]^$+*?.]/g, '\$&')
    .replace(/-/g, '\x2d');

然后让我们编写一个接受模板和占位符的函数。我们应该像这样从占位符创建一个正则表达式:

const regex = new RegExp("(" + Object.keys(placeholders).map(escapeRegex).join("|") + ")", "g");

对于占位符对象中的每个键,我们转义特殊字符,并用 | 将它们全部连接起来,将结果括在括号中。然后我们给它 g (全局)标志,以便它为我们提供所有匹配项。

对于您来说,生成的正则表达式类似于:

/(\{client.avatar\}|\{client.username\}|...|\{guild.id\})/g

最后,我们要使用此正则表达式并将所有出现的占位符替换为其对应的值。

return template.replace(regex, (_, match) => placeholders[match]);

所以完整的代码如下所示:

const escapeRegex = (string) => string
    .replace(/[|\{}()[\]^$+*?.]/g, '\$&')
    .replace(/-/g, '\x2d');

function fill(template, placeholders) {
    const regex = new RegExp("(" + Object.keys(placeholders).map(escapeRegex).join("|") + ")", "g");

    return template.replace(regex, (_, match) => placeholders[match]);
}

用法示例:

fill("My name is {name}!", { "{name}": "Kelly" }); // "My name is Kelly!"

您可以尝试一下 here,只需单击 link 并点击“运行”。在右边你会看到输出。


为什么我们不使用这样的东西:

template
    .replaceAll("{client.avatar}", placeholders["{client.avatar}"])
    .replaceAll("{client.username}", placeholders["{client.username}"])
    .replaceAll(...)
    // ...

嗯,首先,这不是真正的 DRY,是吗?您可以使用循环来帮助解决这个问题:

for (const [place, value] of Object.entries(placeholders)) {
    template = template.replaceAll(place, value);
}

但是有一个问题!如果某个占位符的值包含另一个占位符怎么办?然后您将用新值替换新的占位符!这可能会导致意外行为,这就是我建议改用此解决方案的原因;立即替换 所有 个匹配项,防止这种情况发生。

占位符:

{bar} => i love {foo}
{foo} => 42

然后:

say {bar}
say i love {foo}
say i love 42

呃哦!