С помощью такой строки, как {float: 'null', another: 'foo'}
, я хотел бы получить каждый набор пар ключ/значение, чтобы группы выводили float
, null
, а также another
и foo
.
Мое текущее регулярное выражение: /\{(?<set>(?<key>\w*)\s*\:\s*(?<value>.*)?\s?)*\}/g
Он правильно захватывает ключ, но все, что находится после запятой, получает его как значение. Я использую именованные группы главным образом просто для ясности. Не могу понять, как извлечь каждую пару ключ/значение, особенно если их несколько.
Спасибо за любую помощь
В настоящее время пытаюсь /\{(?<set>(?<key>\w*)\s*\:\s*(?<value>.*)?\s?)*\}/g
, но результат:
группа «набор»: float: 'null', another: 'foo'
(правильно)
группа «ключ»: float
(правильно)
значение группы: 'null', another: 'foo'
(неверно, я хочу просто null
)
Хотелось бы, чтобы он захватывал все пары ключ/значение, если это возможно.
Измените для большей ясности:
Мой конкретный вариант использования — анализ Markdown и подключение его к пользовательским компонентам в Svelte, где я хочу контролировать возможность сбора реквизитов из синтаксиса markdown на изображении. Судя по тому, что я собрал в Интернете о присвоении атрибутов изображению, оно должно выглядеть примерно так:
![Alt Text]https://<fullurl>.jpg "This is hover text"){prop1: 'foo', prop2: 'bar', float: true}
Причиной регулярного выражения является анализ строки уценки. Это не JSON, и я ничего не получу, следуя семантике JSON ("
на ключе)
Это не «похоже на JSON», это JSON. Я думаю, что лучшим подходом было бы просто использовать JSON.parse()
, а затем заменить все 'null'
на null
. При необходимости вы можете использовать Object.entries() для результата.
Зачем использовать регулярное выражение? У вас уже есть пары «ключ-значение» в вашем объекте?
@Peter B: разве json не должен использовать только двойные кавычки? И вокруг значений ("foo"
, а не 'foo'
), и имен полей ("another"
, а не another
)? (Я никогда даже не читал спецификацию json. Так что не уверен на 100%. Но я знаю, что мне часто приходилось заменять кавычки двойными кавычками, чтобы избежать ошибок, как в Python, так и в JS Firefox. Так что на практике использование парсеров json не помогло бы. не работаю с такой строкой)
Конечно, это не меняет того факта, что было бы гораздо разумнее использовать существующий формат с существующим парсером, чем пытаться заново изобрести колесо с помощью регулярных выражений и, вступая в бесконечный цикл, «наткнуться на случай, когда это не так». t работает → восстановить регулярное выражение "
@chrslg Упс, ты действительно на 100% прав. Так что на самом деле это не JSON.... Мои извинения всем.
На самом деле это похоже на объект javascript. (Можно сказать, нотация объекта JavaScript. Но не нотация объекта JavaScript). Но я бы, конечно, не посмел произнести это слово eval
, это было бы зло :D
Добавил правку для большей ясности. То, что кажется объектом, является частью синтаксиса уценки, который я пытаюсь проанализировать; это строка. Возможно, я могу просто использовать JSON.parse() для всего совпадения, поскольку у меня есть доступ ко всему совпадению, хотя было бы неплохо не требовать '
или "
для ключей.
@chrslg, мне просто нравится твое сопоставление eval()
и «зла». Я бы тоже никогда, никогда, никогда... не мечтал произнести такое слово. 🤣 Тем более не здесь! Гораздо лучше изобрести велосипед и написать еще один парсер. 😉
Прежде всего исправьте входную строку. Вы не можете ожидать, что 'null'
не даст вам ложных срабатываний. Что, если вдруг кто-то действительно захочет передать строку «null»? Не следует конвертировать его в примитив. Вместо этого не заключайте его в кавычки.
Попробуйте использовать это длинное регулярное выражение JavaScript:
/(?<key>\w*)\s*:\s*(?<value>(?<quote>["'])(?:\\.|.)*?\k<quote>|(?<number>[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)|(?<constant>true|false|null))/g
В действии (посмотрите на всю страницу, если нет, то не все видно):
const regexKeyValue = /(?<key>\w*)\s*:\s*(?<value>(?<quote>["'])(?:\\.|.)*?\k<quote>|(?<number>[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)|(?<constant>true|false|null))/g;
document.getElementById('search').addEventListener('click', function () {
const input = document.getElementById('input').value;
let match,
i = 1,
output = [];
while ((match = regexKeyValue.exec(input)) !== null) {
console.info(`Match n°${i} : ` + match[0]);
console.info('match.groups =', match.groups);
// If the value is starting with quotes, then unquoted it and
// also replace all the escape sequences (ex: "\\n" should become "\n").
let value = match.groups.value;
// If it's double quotes, let's use JSON.parse() as it will handle everything.
if (value.match(/^"/)) {
value = JSON.parse(value);
}
// If it's simple quotes, we can't use JSON.parse() so we have to convert
// it to a double-quoted string before.
else if (value.match(/^'/)) {
value = value
// 1) Remove the simple quotes around.
.replace(/^'|'$/g, '')
// 2) Replace all \' by '.
// We have to search for all backslashes to handle also an escaped backslash.
.replace(/\\(.)/g, function (fullMatch, afterBackslash) {
if (afterBackslash === "'") {
return "'";
} else {
return fullMatch;
}
})
// 3) Escape all double quotes (" becomes \").
.replace(/"/g, '\\"');
// 4) Now use JSON.parse();
value = JSON.parse(`"${value}"`);
}
// If it's a number or a constant, then convert the string to this real JS value.
if (typeof match.groups.number !== 'undefined' ||
typeof match.groups.constant !== 'undefined') {
value = JSON.parse(match.groups.value);
}
console.info('value =', value);
output.push(
`Match n°${i++} :\n` +
` Key : ${match.groups.key}\n` +
` Value : ${value}\n`
);
}
document.getElementById('output').innerText = output.join("\n");
document.getElementById('label').classList.remove('hidden');
});
textarea {
box-sizing: border-box;
width: 100%;
}
pre {
overflow-y: scroll;
}
.hidden {
display: none;
}
<textarea id = "input" rows = "10">{
float: 'null',
another: "foo",
age: 45,
type: '"simple" \' quote',
comment: "Hello,\nA backslash \\, a tab \t and a \"dummy\" word.\nOk?",
important: true,
weight: 69.7,
negative: -2.5
}</textarea>
<button id = "search">Search for key-value pairs</button>
<p id = "label" class = "hidden">Matches:</p>
<pre><code id = "output"></code></pre>
То же регулярное выражение, с комментариями, с флагом x
PCRE предлагает:
/
(?<key>\w*) # The key.
\s*:\s* # : with optional spaces around.
(?<value> # The value.
# A string value, single or double-quoted:
(?<quote>["']) # Capture the double or single quote.
(?:\\.|.)*? # Backslash followed by anything or any char, ungreedy.
\k<quote> # The double or single quote captured before.
|
# Int and float numbers:
(?<number>[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)
|
# true, false and null (or other constants):
(?<constant>true | false | null)
)
/gx
Или, что еще лучше, в регулярном выражении 101 у вас будут цвета и объяснение. в правой колонке: https://regex101.com/r/bBPvUd/2
Это чертовски кратко, и мне нравится то, что вы сделали с обратной ссылкой на строку, чтобы обеспечить правильное окружение. Я собираюсь использовать ваш ответ, но не могу понять, как захватить строку только без кавычек. Как бы вы это сделали?
Ну, как я это сделал, используя код JavaScript в своем ответе, используя JSON.parse()
. Это потому, что мы хотим, чтобы \n
был символом возврата каретки, а не обратной косой чертой, за которой следовал символ n
. Но если вы хотите быстро захватить содержимое в регулярном выражении, просто добавьте группу захвата между кавычками, но имейте в виду, что захваченное содержимое не является окончательным желаемым значением, поскольку оно не будет оценено как правильная строка. .
Как уже упоминалось в комментариях, eval()
считается «злым» или, по крайней мере, небезопасным. Я точно забыл почему, что-то связано с межсайтовым скриптингом. Однако, если он используется в «безопасной» среде, т.е. е. для предварительной обработки ввода, над которой вы имеете полный контроль, тем не менее, это может быть допустимо.
const md=`Some text and now the image:
![Alt Text]https://<fullurl>.jpg "This is hover text"){prop1: 'foo', prop2: 'bar', float: true}
and some more text.
A new paragraph any yet nother picture
![Alt Text2]https://<fullerURL>.jpg "This is another hover text"){prop1: 'fool', prop2: 'bart', float: false} and this is the end.`;
function unsafeParse(s){
return s.match(/\{[^}]+\}/g).map(t=>eval(`(${t})`));
}
// ouputs an array of all image property objects:
console.info(unsafeParse(md));
Помимо того, что вышеизложенное является «небезопасным», оно не является полностью отказоустойчивым, поскольку значения свойств, содержащие символ «}», вызовут проблемы...
попробуйте включить разделитель для
set
иkey
. Если вы уверены, что это слово, вы можете использовать\w+
или использовать'
, чтобы закончить строку.