Попытка сопоставить все регулярное выражение в строке JavaScript

Я пытаюсь string.matchAll следующую строку:

const text = 'textA [aaa](bbb) textB [ccc](ddd) textC'

Я хочу сопоставить следующее:

  • 1-й: "textA [aaa](bbb)"
  • 2 место: " textB [ccc](ddd)"
  • 3 место: " textC"

ПРИМЕЧАНИЕ: Группы захвата уже присутствуют в regex. Это то что мне нужно.

Это почти работает, но до сих пор я не мог придумать, как сопоставить последнюю часть строки, которая представляет собой просто " textC" и не имеет шаблона [*](*).

Что я делаю неправильно?

const text = 'textA [aaa](bbb) textB [ccc](ddd) textC'
const regexp = /(.*?)\[(.+?)\]\((.+?)\)/g;

const array = Array.from(text.matchAll(regexp));
console.info(JSON.stringify(array[0][0]));
console.info(JSON.stringify(array[1][0]));
console.info(JSON.stringify(array[2][0]));

Обновлено:

Помимо хороших решений, представленных в ответах ниже, это также вариант:

const text= 'textA [aaa](bbb) textB [ccc](ddd) textC'

const regexp = /(?!$)([^[]*)(?:\[(.*?)\]\((.*?)\))?/gm;

const array = Array.from(text.matchAll(regexp));

console.info(array);

попробуйте это: (\w+)\s*(?:[(.+?)]((.+?)))?

SEDaradji 17.06.2019 20:48

Что-то не так с (.+\)) (.+\)) (.+)?

CAustin 17.06.2019 20:51
Мой ответ будет работать для разделения любой строки с любым шаблоном, сохраняя совпадающий текст в левой части разделенного фрагмента. Это работает для вас? Вы уверены, что результат, который вам нужен, это тот, который вы показали в вопросе? Является ли textC заполнителем, и он может быть просто равен word 1 word 2 and word 3 and so on...., и вам нужно получить этот текст как один элемент в результирующем массиве?
Wiktor Stribiżew 18.06.2019 11:03
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
3
1 438
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Потому что третьего матча нет. После первых двух совпадений в строке остается только «текст C»:

https://regex101.com/r/H9Kn0G/1/

чтобы исправить это, сделайте всю вторую часть необязательной (также обратите внимание на начальную \w вместо ., чтобы эта точка не поглощала всю строку, а также скобки «только для группировки», используемые для окружения необязательной части, что сохраняет ваши группы соответствия тоже самое):

(\w+)(?:\s\[(.+?)\]\((.+?)\))?

https://regex101.com/r/Smo1y1/2/

Слово характер слишком ограничительно для меня. Я хочу сопоставить ЛЮБУЮ строку, за которой следует шаблон [+](+), и если несколько шаблонов [+](+) написаны вместе один за другим, я хочу сопоставить их 1 на 1.

cbdeveloper 18.06.2019 11:05

чтобы сопоставить буквально все до следующей скобки, попробуйте следующее: ((?:(?!\[).)+)(?:\s?\[(.+?)\]\((.+?)\))?. regex101.com/r/HqbTpU/1 Я добавил «закаленный токен» с этим отрицательным прогнозом, очевидно, более сложным.

Scott Weaver 18.06.2019 11:17

@ScottWeaver Пожалуйста, никогда не используйте закаленный жадный токен, когда вы ограничиваете . одним символом. (?:(?!\[).)+ (почти) = [^[]+. На самом деле это равно чему-то вроде [^[\n\r]+. Класс отрицательных символов работает намного быстрее.

Wiktor Stribiżew 18.06.2019 11:19

да, это немного проще и работает так же. regex101.com/r/vMFKXH/1

Scott Weaver 18.06.2019 11:21

Кроме того, ваше регулярное выражение легко сломать, если перед построением [...](...) стоят "отдельные" скобки.

Wiktor Stribiżew 18.06.2019 11:21

каждое решение регулярного выражения легко сломать. требуемая надежность до OP - иногда регулярное выражение лучше, иногда требуются алгоритмические подходы.

Scott Weaver 18.06.2019 11:22

Точное решение, которое мне нужно, это ([^[]*)(?:\[(.+?)\]\((.+?)\))? , но оно соответствует последней позиции как нулевая длина. Если я изменю его на ([^[]+)(?:\[(.+?)\]\((.+?)\))?, требуя, чтобы первая группа имела хотя бы 1 символ, я избавлюсь от совпадения последней позиции, но я не могу сопоставить несколько шаблонов второй группы, когда они вместе, как [+](+)[+](+)[+](+)... Любой простой способ получить избавиться от этой последней позиции нулевой длины?

cbdeveloper 18.06.2019 11:29

Кажется, я нашел то, что мне нужно: (?!$)([^[]*)(?:\[(.*?)\]\((.*?)\))?

cbdeveloper 18.06.2019 11:35

множественный [+](+)...N — это другая игра, верно? потому что тогда это произвольная длина, которая не может быть правильно захвачена в группы соответствия. (вы можете захватить всю последовательность, а затем выполнить еще один анализ)

Scott Weaver 18.06.2019 11:41

Решение 1. Разделение путем сопоставления

Вы можете разделить, сопоставив шаблон и получив подстроки из предыдущего индекса до конца совпадения:

const text = 'textA [aaa](bbb) textB [ccc](ddd) textC'
const regexp = /\[[^\][]*\]\([^()]*\)/g;
let m, idx = 0, result=[];
while(m=regexp.exec(text)) {
  result.push(text.substring(idx, m.index + m[0].length).trim());
  idx = m.index + m[0].length;
}
if (idx < text.length) {
  result.push(text.substring(idx, text.length).trim())
}
console.info(result);

Примечание:

  • \[[^\][]*\]\([^()]*\) соответствует [, любым 0+ символам, кроме [ и ][^\][]*), затем ](, затем 0+ символов, кроме ( и )[^()]*), а затем ) (см. демонстрация регулярных выражений)
  • Группы захвата удаляются, но при необходимости их можно восстановить и сохранить в результирующем массиве отдельно (или в другом массиве).
  • .trim() добавлен, чтобы избавиться от начальных/конечных пробелов (удалите, если не нужно).

Решение 2. Сопоставление необязательного шаблона

Идея состоит в том, чтобы сопоставить любые символы перед имеющимся у вас шаблоном, а затем сопоставить либо ваш шаблон, либо конец строки:

let result = text.match(/(?!$)(.*?)(?:\[(.*?)\]\((.*?)\)|$)/g);

Если в строке могут быть разрывы строк, замените . на [\s\S] или рассмотрите этот шаблон:

let result = text.match(/(?!$)([\s\S]*?)(?:\[([^\][]*)\]\(([^()]*)\)|$)/g);

См. демонстрация регулярных выражений.

Демонстрация JS:

const text = 'textA [aaa](bbb) textB [ccc](ddd) textC'
const regexp = /(?!$)(.*?)(?:\[(.*?)\]\((.*?)\)|$)/g;

const array = Array.from(text.matchAll(regexp));
console.info(JSON.stringify(array[0][0]));
console.info(JSON.stringify(array[1][0]));
console.info(JSON.stringify(array[2][0]));

Сведения о регулярном выражении

  • (?!$) - не в конце строки
  • (.*?) - Группа 1: любые символы 0+, кроме символов разрыва строки, как можно меньше (измените на [\s\S]*?, если могут быть разрывы строк, или добавьте модификатор s, поскольку вы нацелены на ECMAScript 2018)
  • (?:\[(.*?)\]\((.*?)\)|$) - любой из двух вариантов:
    • \[(.*?)\]\((.*?)\) - [, группа 2: как можно меньше любых символов 0+, кроме символов разрыва строки, ](, группа 3: как можно меньше любых символов 0+, кроме символов разрыва строки, и )
    • | - или
    • $ - конец строки.

Извините, я слишком долго давал отзыв. Ваш ответ кажется слишком рабочим (в нем отсутствуют некоторые пробелы из textB и textC), но моя главная проблема в том, что он показался мне не очень читаемым. Я бы хотел, чтобы лучше работать с regex и методом matchAll. Спасибо.

cbdeveloper 18.06.2019 11:07

У меня есть этот regex, который в основном работает, но он соответствует совпадению нулевой длины в последней позиции. /([^\[]*)?(?:\[(.+?)\]\((.+?)\))?/gm

cbdeveloper 18.06.2019 11:08

@ cbdev420 Итак, вы хотите использовать нечитаемое регулярное выражение? :) /(?=[\s\S])([\s\S]*?)(?:\[([^\][]*)\]\(([^()]*)\)|$)/g

Wiktor Stribiżew 18.06.2019 11:09

Я думаю, что регулярное выражение, которое я получил сейчас, которое почти работает, довольно читабельно. ([^\[]*)?(?:\[(.+?)\]\((.+?)\))? В основном группа соответствует любому символу, кроме левой скобки [, если это возможно, то я стараюсь соответствовать шаблону [+](+). Но я согласен, что удобочитаемость — это точка зрения. Это действительно личное.

cbdeveloper 18.06.2019 11:12

Могу ли я изменить текущее регулярное выражение, чтобы избавиться от последнего совпадения? Или нужно было идти совсем другим путем? Спасибо большое за вашу помощь.

cbdeveloper 18.06.2019 11:13

@ cbdev420 Я добавил в ответ решение с фиксированным регулярным выражением. С двумя вариациями

Wiktor Stribiżew 18.06.2019 11:14

Спасибо! Я все еще тестирую некоторые варианты, но ваш точно работает.

cbdeveloper 18.06.2019 11:30

@cbdev420 (?=.) или (?!$) являются синонимами. См. этот мой ответ.

Wiktor Stribiżew 18.06.2019 11:44
Ответ принят как подходящий

Это то, что я в итоге использовал:

const text= 'textA [aaa](bbb) textB [ccc](ddd) textC'

const regexp = /(?!$)([^[]*)(?:\[(.*?)\]\((.*?)\))?/gm;

const array = Array.from(text.matchAll(regexp));

console.info(array);

Другие вопросы по теме