Я хочу создать простой текстовый шаблон, который позволит определять заполнители с помощью {0}
, аналогично тому, что .Net делает с помощью метода string.format.
В основном я хочу этого:
format("{0}", 42), // output `42`
format("{0} {1}", 42, "bar"), // output `42 bar`
format("{1} {1}", 42, "bar"), // output `bar bar` ({0} ignored)
format("{{0", 42), // output `{0` (`{{` is an escaped `{`)
format("{{{0}", 42), // output `{42` : an escaped brace and the formatted value
format("Mix {{0}} and {0}", 42), // outputs `Mix {0} and 42`
format("Invalid closing brace }"), // should fail, since the closing brace does close an opening one
format("Invalid placeholder {z}"), // should fail, not an integer
format("{0}", "With { in value"), // output `With { in value`, inner { should be broke the format
Я пытаюсь поиграть с регулярным выражением и выполнить возврат, чтобы справиться со экранированными фигурными скобками.
function format(template: string, ...values: unknown[]): string {
const regex = /(?!({{)+){(\d+)}(?<!(}}))/gm;
return template.replace(regex, ([, index]) => {
const valueIndex = parseInt(index, 10);
if (valueIndex >= values.length) throw new Error("Not enough arguments")
return String(values[valueIndex]);
});
}
console.info([
format("{0}", 42), // output `42`
format("{0} {1}", 42, "bar"), // output `42 bar`
format("{1} {1}", 42, "bar"), // output `bar bar` ({0} ignored)
format("{{0", 42), // output `{0` (`{{` is an escaped `{`)
format("{{{0}", 42), // output `{42` : an escaped brace and the formatted value
format("Mix {{0}} and {0}", 42), // outputs `Mix {0} and 42`
format("Invalid closing brace }"), // should fail, since the closing brace does not close an opening one
format("Invalid placeholder {z}"), // should fail, not an integer
format("{0}", "With { in value"), // output `With { in value`, inner { should be broke the format
]);
try {
format("{0} {1}", 42); // throw error because not enough argument are passed
} catch (e) {
console.info(e.message);
}
Тем не менее, я изо всех сил пытаюсь правильно заменить экранированные скобки одной скобкой.
Как это исправить?
это должно потерпеть неудачу. Разрешены только двойные {{ и }}, если они не являются заполнителем.
Должен ли он потерпеть неудачу в том смысле, что должен вызвать ошибку? Или он должен просто не соответствовать и заменить его? Но тогда замените }}
на один...?
должен бросить. В противном случае произойдет утечка неправильных шаблонов.
Хорошо, тогда, возможно, это следует упомянуть в вопросе с тестовым примером. Все же я считаю такое поведение несбалансированным, ведь на зеркальный корпус не бросишь {{0}
. Разве не было бы более элегантно, если бы это было такое же поведение?
Обновите вопрос, чтобы уточнить. {{0}
тоже должно потерпеть неудачу
Хорошо, думаю, я понял ожидаемую логику. Дайте мне знать, если я неправильно это интерпретировал.
Вы можете использовать
(?<=(?<!\{)(?:\{\{)*)\{\d+}(?!(?:}})*}(?!}))
Посмотрите демонстрацию регулярных выражений .
Подробности:
(?<=(?<!\{)(?:\{\{)*)\{
- символ {
, который не экранирован (не должно быть {
, за которым следует ноль или более двойных {
символов)\d+
}(?!(?:}})*}(?!}))
- символ }
, который не экранирован (не должно быть }
, которому предшествует ноль или более двойных }
символов)Часть .replaceAll('{{','{').replaceAll('}}','}')
завершает трансформацию.
Посмотрите демо-версию JS:
function format(template, ...values) {
const regex = /(?<=(?<!\{)(?:\{\{)*)\{\d+}(?!(?:}})*}(?!}))/g;
return template.replace(regex, ([, index]) => {
const valueIndex = parseInt(index, 10);
if (valueIndex >= values.length) throw new Error("Not enough arguments")
return String(values[valueIndex]).replaceAll('{{','{').replaceAll('}}','}');
});
}
console.info([
format("{0}", 42), // output `42`
format("{0} {1}", 42, "bar"), // output `42 bar`
format("{1} {1}", 42, "bar"), // output `bar bar` ({0} ignored)
format("{{0", 42), // output `{0` (`{{` is an escaped `{`)
format("{{{0}", 42), // output `{42` : an escaped brace and the formatted value
format("Mix {{0}} and {0}", 42), // outputs `Mix {0} and 42`
]);
try {
format("{0} {1}", 42); // throw error because not enough argument are passed
} catch (e) {
console.error(e);
}
ПРИМЕЧАНИЕ. Вам необходимо «подготовить» значения, передаваемые в функции, так, чтобы фигурные скобки были экранированы, т. е. просто используйте value.replaceAll('{','{{').replaceAll('}','}}')
.
@trincot format("{0} {1}", 42, "b{ar")
дает ожидаемый результат 42 b{ar
, так какой вариант использования вы упомянули?
Я думал о format("{0}", "{{")
, где восстановительная стоимость также будет уменьшена вдвое.
@trincot, это реализация, все, что нужно, это отправить их с экранированными фигурными скобками или просто «подготовить» их с помощью .replaceAll('{','{{').replaceAll('}','}}')
Обратите внимание, что существует также проблема (исходящая из кода спрашивающего), из-за которой заполнители с двумя или более цифрами не заменяются правильно: format("{12}", 0,1,2,3,4,5,6,7,8,9,10,11,12)
.
Я бы предложил заменить двойные скобки в том же вызове replace
. Для обнаружения «синтаксических» ошибок я бы предложил, чтобы, когда в качестве остатка обнаруживается только одна скобка (после потенциальной замены пар из них), которая не может сочетаться с заполнителем, выдавать ее как синтаксическую ошибку:
function format(template, ...values) {
const regex = /{(\d+)}|([{}])(\2?)/g;
return template.replace(regex, (_, index, brace, escaped) => {
if (escaped) return brace;
if (brace) throw new Error("Unescaped literal brace");
if (+index >= values.length) throw new Error("Not enough arguments");
return values[+index];
});
}
const tests = [
["{0}", 42], // output `42`
["{0} {1}", 42, "bar"], // output `42 bar`
["{1} {1}", 42, "bar"], // output `bar bar` ({0} ignored)
["{{0", 42], // output `{0` (`{{` is an escaped `{`)
["{{{0}", 42], // output `{42` : an escaped brace and the formatted value
["Mix {{0}} and {0}", 42], // outputs `Mix {0} and 42`
["Invalid closing brace }"], // should fail, since the closing brace does not close an opening one
["Invalid placeholder {z}"], // should fail, not an integer
["{0}", "With { in value"], // output `With { in value`, inner { should be broke the format
["{0} {1}", 42], // throw error because not enough argument are passed
];
for (const [test, ...args] of tests) {
try {
console.info(format(test, ...args));
} catch (e) {
console.info("Error:", e.message);
}
}
Нет необходимости в утверждениях обхода, поскольку нет риска, что при возврате эти парные скобки будут разбиты на части неправильно.
Обратная ссылка \2
служит для указания того, что была сопоставлена двойная фигурная скобка, указывая на экранированную фигурную скобку (либо {
, либо }
). ?
является жадным, но если такой повторяющейся скобки нет, мы зафиксировали одну скобку, которая нарушает синтаксис.
Обновил мой ответ в свете обновления вопроса, который указывает на то, что должна быть выдана ошибка, если скобки не экранируются, хотя они на самом деле должны быть.
потрясающий. Спасибо !
Что должно произойти с
{0}}
?