Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Замена в регулярном выражении подстрок (https://javascript.ru/forum/misc/77590-zamena-v-regulyarnom-vyrazhenii-podstrok.html)

Rust 24.05.2019 11:06

Замена в регулярном выражении подстрок
 
Подскажите - как решаются подобные классы задач:
С помощью регулярного выражения в совпадающем фрагменте найти и заменить текст, соответствующий n-ной скобке.

Например:
в строке
"1285423808063"

в соответствии с регулярным выражением
/[^3](8)(0)[6-8]/

заменить 8 на *
и 0 на +
чтобы получилось
"128542380*+63"

рони 24.05.2019 12:41

Rust,
var str = "1285423808063";
str = str.replace(/(380)(\d{2})/, "$1*+");
alert(str);

Rust 24.05.2019 12:46

Нужно общее решение. С частными всё понятно

рони 24.05.2019 12:47

Цитата:

Сообщение от Rust
Нужно общее решение.

уточните вопрос?

Rust 24.05.2019 12:55

Для понимания, нужно дописать
function(v,s) {
  return v.replace(s, function(f) {
    ...
  });
}
так, чтобы при любой строке v и любом регулярном выражении s текст, соответствующий n-ной скобке обрамлялся тегами:
<span class=Pn>$n</span>

рони 24.05.2019 13:00

Цитата:

Сообщение от Rust
Для пониман

не понимаю, можно примеры, строка на входе и строка на выходе.

рони 24.05.2019 13:07

Rust,
возможно вы хотели так
var str = "1285423808063";
function r(v,s,n) {
  return v.replace(s, function(f) {
     return --n ? f : '"'+f+'"'
  });
}
str = r(str, /\d{2}/g,3);
alert(str);

Rust 24.05.2019 13:27

строка:
Ну что сказать? -12.85 - это отрицательное число, а 0.86 - положительное

регулярное выражение:
/[^-\d.](\d+)(\.\d+)?/g


Результат:
Ну что сказать? -12.85 - это отрицательное число, а <span class=P1>0</span><span class=P2>.86</span> - положительное

Rust 24.05.2019 13:29

Повторяю. Строка и регулярка могут быть любой.
Обработку пишу я. Мне нужен общий метод.

рони 24.05.2019 13:42

Цитата:

Сообщение от Rust
Мне нужен общий метод.

пост #7

Rust 24.05.2019 14:04

Не n-е совпадение шаблона, а n-я скобка!
Метод должен работать и без флага g

рони 24.05.2019 14:19

Rust
плохо представляю что вам нужно.

Rust 24.05.2019 14:55

Цитата:

Сообщение от рони (Сообщение 508213)
плохо представляю что вам нужно.

Мне нужна функция от двух параметров - строка и регулярное выражение.
Регулярное выражение может содержать некоторое количество круглых скобок, обозначающих атомы регулярного выражения. В том числе и вложенные друг в друга!

Функция должна найденные атомы регулярного выражения обрамлять указанными символами.

рони 24.05.2019 15:18

Rust,
может кто-то другой сможет вам помочь, для меня, вашего описания по прежнему недостаточно.

Rust 24.05.2019 15:29

Может быть проблема в терминологии? Атом == группа вот в этом смысле

Alexandroppolus 24.05.2019 15:53

Rust,
Регулярка вообще любая?

Может ли там быть, например, вот такая хрень: (\d)+
т.е. то что в скобках повторяется несколько раз и надо каждое повторение отдельно обработать? И может ли быть такое для вложенных скобок? Типа (...(\d)+...)*

Сколько может быть скобок в регулярке?

Это должно быть кроссбраузерно, или код только для Хрома и node.js?


Я, кажется, понял, что надо, но задача крепкая, если в совсем общем виде.

рони 24.05.2019 15:56

Цитата:

Сообщение от Rust
Функция должна найденные атомы регулярного выражения обрамлять указанными символами.

Цитата:

$n или $nn, где n/nn - десятичные цифры
Вставляет n-ю скобку в совпадении,
что мешает написать нужный шаблон замены?

Alexandroppolus 24.05.2019 16:00

Цитата:

Сообщение от рони
что мешает написать нужный шаблон замены?

Автору нужен код, который этот шаблон автоматически сгенерирует. Но просто шаблоном тут не всегда можно будет обойтись.

рони 24.05.2019 16:04

Цитата:

Сообщение от Alexandroppolus
который этот шаблон автоматически сгенерирует.

чтобы это значило? подожду решения.

рони 24.05.2019 16:06

:) пока так вариант с неавтоматическим шаблоном...

var str = "Ну что сказать? -12.85 - это отрицательное число, а 0.86 - положительное";
function r(v,s) {
  return v.replace(s, function(...f) {
     return  `${f[1]}<span class=P1>${f[2]}</span><span class=P2>${f[3]}</span>`
  });
}
str = r(str, /([^-\d.])(\d+)(\.\d+)?/g);
alert(str);

Alexandroppolus 24.05.2019 16:18

Цитата:

Сообщение от рони
пока так вариант с неавтоматическим шаблоном...

В твоем примере самый простой случай: регуляра состоит только из линейной последовательности групп (нет групп, вложенных одна в другую), после групп нет кванторов * и т.п., между группами нет ничего. Под такое дело шаблон легко составляется, его можно даже автоматически собрать.

Rust 24.05.2019 16:44

Alexandroppolus,
Регулярка любая. Ограничений нет.
Хрень может быть любая, в том числе и (...)+
Замен она влечёт ровно столько, сколько даёт match
Вложенность групп тоже может быть:
"1234567890"
/\d(\d(\d)\d)\d/g
результ ат:
<span class=P0>1<span class=P1>2<span class=P2>3</span>4</span>5</span><span class=P0>6<span class=P1>7<span class=P2>8</span>9</span>0</span>


да, совместимость со всеми браузерами очень желательна!

Alexandroppolus 24.05.2019 16:58

Rust,
"1234"
/(\d){2}/g

<span class=P0><span class=P1>1</span><span class=P1>2</span></span><span class=P0><span class=P1>3</span><span class=P1>4</span></span>

Так?

рони 24.05.2019 17:15

Цитата:

Сообщение от Alexandroppolus
его можно даже автоматически собрать.

:)
let str = "Ну что сказать? -12.85 - это отрицательное число, а 0.86 - положительное";
function r(v,s) {
  return v.replace(s, function(...f) {
     return f.slice(1,-2).reduce((str, reg, i) => str.replace(reg, `<span class=P${++i}>$&</span>`), f[0])
  });
}
str = r(str, /[^-\d.](\d+)(\.\d+)?/g);
alert(str);

Malleys 24.05.2019 17:25

Цитата:

Сообщение от рони
плохо представляю что вам нужно.

Цитата:

Сообщение от рони
чтобы это значило? подожду решения.

Смотрите, например код...
"aaabbbcc".match(/bb(b).(c)/)
возвращает...
Код:

{
    "0": "bbbcc",
    "1": "b",
    "2": "c",
    groups: undefined,
    index: 3,
    input: "aaabbbcc",
    length: 3,
    __proto__: Array.prototype
}

а автору нужно, чтобы это было так... Чтобы...
"aaabbbcc".match(/bb(b).(c)/)
возвращал...
Код:

{
    "0": "bbbcc",
    "1": { valueOf() { return "b" }, index: 5 },
    "2": { valueOf() { return "c" }, index: 7 },
    groups: undefined,
    index: 3,
    input: "aaabbbcc",
    length: 3,
    __proto__: Array.prototype
}

Тогда можно будет сказать, где в исходном тексте находится найденная группа

Так что, рони, ваш код из поста №24 работает неправильно... Вот с такими данными видно...
let str = "aaaa";
function r(v,s) {
  return v.replace(s, function(...f) {
     return f.slice(1,-2).reduce((str, reg, i) => str.replace(reg, `<span class=P${++i}>$&</span>`), f[0])
  });
}
str = r(str, /aa(a)a/g);
alert(str);

рони 24.05.2019 17:30

Malleys,
спасибо!

Alexandroppolus 24.05.2019 18:47

Размышления, для общего случая:
Придется вручную парсить регулярку, чтобы понять её структуру и при необходимости что-то в ней поменять.

1) Элементарный случай, линейная регулярка только из групп: /(...)(...)(...)/ - , оставляем как есть, замена по паттерну "<P1>$1</P1><P2>$2</P2><P3>$3</P3>"

2) Линейная регулярка с подстроками вне групп: /(...)...(...)/ - все необернутое оборачиваем в группы, получив /(...)(...)(...)/, замена по паттерну "<P1>$1</P1>$2<P2>$3</P2>", тут для $2 никаких тегов не добавлено, потому что это не было группой, а для $3 указаны теги P2, это была 2 группа изначально. Т.е. понадобится таблица соответствий, ибо номера групп сдвигаются.

3) Вложенные группы. /(...)(...(...)...(...)...)(...)/ - если нет ничего из п.4, то вторую группу можно просто растянуть в линейную по мотивам п.2: /(...)(...)(...)(...)(...)(...)(...)/, опять же с таблицей соответствий сдвинувшихся номеров.

4) Звездочки, плюсики, знаки вопроса, фигурные скобки. /...(regex)+.../ - делаем так: /(...)((?:regex)+)(...)/, в методе replace второй параметр - функция, и в ней матч для второй группы отдельно реплейсим регуляркой /regex/g, по тем же правилам, т.е. будет рекурсия.

При составлении таблицы соответствий из п.2 не забыть про обратные ссылки в регулярке (\1, \2 и т.д.), их тоже поправить.

Что делать с lookahead в п.4, пока не совсем понятно.

Malleys 24.05.2019 19:33

Alexandroppolus, если вы рег. выр. из п. 3 /(...)(...(...)...(...)...)(...)/ преобразуете, как вы описали, то вы потеряете группу №2. Оно всё-таки должно стать так: /(...)((...)(...)(...)(...)(...))(...)/

Из п. 4 /...(regex)+.../ лучше так: /(...)(regex)+.../, ведь зачем вам группы, которые не используются.

Alexandroppolus 24.05.2019 19:48

Цитата:

Сообщение от Malleys
Alexandroppolus, если вы рег. выр. из п. 3 /(...)(...(...)...(...)...)(...)/ преобразуете, как вы описали, то вы потеряете группу №2. Оно всё-таки должно стать так: /(...)((...)(...)(...)(...)(...))(...)/

Точно! Всё-таки придется п.3 рекурсивно обрабатывать.
Цитата:

Сообщение от Malleys
Из п. 4 /...(regex)+.../ лучше так: /(...)(regex)+.../, ведь зачем вам группы, которые не используются.

Не, тут другая идея.

Если обобщить пп. 1-4, то суть вот в чем: мы всегда приводим регекс к виду из п.1 - последовательность групп, после каждой из которых нет + и прочего. Но если в этом регексе какая-то из групп содержит вложенные группы, то мы матч для неё обрабатываем по тем же правилам, как всю строку, иными словами, рекурсия. А п.4 - как тот же + загнать в линейную форму.

Комплексный пример:

регекс /a(b)c(d(e))(fg)+/

после преобразования получим 3 регекса:
1) Общий /(a)(b)(c)(de)((?:fg)+)/, где обертываем тегами группы 2 (с исходным номером 1, т.е. тег <P1>), 4 (тег <P2>). Вложенные группы расформировываем или нейтрализуем как ?:, они тут не нужны.

2) Матч 4 группы при этом обрабатываем регуляркой /d(e)/g, которая превратится в /(d)(e)/ и дальше рекурсия не пойдет. Здесь группа для (е) обертывается как <P3>

3) Матч 6 группы, обрабатываем регуляркой /(fg)/g, группа (fg) обертывается как <P4>, дальше рекурсия не пойдет.


https://www.npmjs.com/package/regulex - парсилка

Rust 27.05.2019 09:48

Malleys, вы предельно ясно сформулировали мои потребности. Спасибо!


Часовой пояс GMT +3, время: 05:56.