Форум программистов, компьютерный форум, киберфорум
Наши страницы
JavaScript: RegExp
Войти
Регистрация
Восстановить пароль
 
user7845
5 / 5 / 0
Регистрация: 07.06.2012
Сообщений: 98
#1

Анализ регулярного выражения

04.02.2018, 17:43. Просмотров 479. Ответов 8
Метки нет (Все метки)

Имеется регулярное выражение для вставки разделителя в числа. Например на входе 12345678 на выходе 12,345,678

Javascript
1
2
3
  function numberWithCommas(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',' );
  }
Не могу понять как работает это регулярное выражение. По отдельности, вроде понимаю, что означает каждый символ, но как это всё вместе работает и почему не могу понять.

\B cоответствует несловообразующей границе. Несловообразующая граница соответствует позиции, в которой предыдущий и следующий символы являются символами одного типа. (Вроде понятно)

Движок регулярных выражений идет и по строке и по шаблону. Находит в строке первое соответствие. Если в качестве примера взять строку 12345678, то первое соответствие будет граница между 1 и 2. Что дальше?

Дальше движок идет по шаблону, и видит в шаблоне скобочную группу с символами ?=

?= это выражение работает по формуле x(?=y) где соответствие случится, только если после x идет y

Теперь движок будет смотреть что стоит после знаков ?= и искать это в строке. Если найдет, то соответствие случится, если не найдет, значит соответствие не случится.

У нас после знака стоит выражение (\d{3})+ т.е. нужно найти три цифры, потом еще три, потом еще три и так до конца строки. Получается на этом этапе он находит цифры 234567

Движок продолжает идти по шаблону. Видит в шаблоне (?!\d). Это значит, что после 234567 не должна стоять цифра, а в нашем случае она стоит, значит ничего не происходит, соответствие не найдено.

Всё шаблон и строка пройдены до конца. А так как стоит флаг глобального поиска, то весь вышеописанный алгоритм действий повторяется, но только уже не с первого, а со второго символа. И так много раз. И в определенные моменты будут найдены "границы" на которые функция replace поставит запятые.

Вот. Вроде что-то, как-то смутно понимается, но сложно.

Если кто-то опишет понятнее, буду благодарен.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
04.02.2018, 17:43
Ответы с готовыми решениями:

Составление регулярного выражения
Всем привет. Второй день бьюсь с регуляркой, уже бесить начинает. Есть HMTL в...

Переменная внутри регулярного выражения
function Moscow () { var fcity = document.getElementById('100'); var length...

Выбрать подстроку с использованием регулярного выражения
Привет всем )) Подскажите мне: вот есть у меня срока var str =...

Написание регулярного выражения для pattern
Всем привет. Как задавать на паттерн чтобы текст заканчивался на @ и кроме...

Каков смысл регулярного выражения в данном коде?
Здравствуйте! можете подсказать по коду. страница сохраняет куки. а вот какой...

8
Svsh2015
132 / 108 / 22
Регистрация: 23.06.2015
Сообщений: 338
11.02.2018, 17:47 #2
user7845, Вы ,по-моему впервые на форумах создали тему по логике работы регулярных выражений,причем в сложном аспекте применения регулярных выражений,дело в том,что совпадение может быть не только с фрагментом текста,но также и с позицией в тексте,а при замене,когда не было совпавшего текста,а была найдена только позиция совпадения,замещающий текст(у нас запятая) подставляется в эту позицию.

Процесс сопоставления т шаблону обычно начинается с начала,а в нашем случае в шаблоне задано другое \B( то есть начинаем поиск в позиции между 1 и 2) и с начала шаблона.
Имеются два подшаблона-позитивная опережающая проверка (?=шаблон) и негативная опережающая проверка(?!шаблон)
У Вас в целом правильная логика рассуждений,поэтому я только подкорректировал Ваш текст:




Если в качестве примера взять строку 12345678, то первая позиция будет между 1 и 2.

Дальше движок идет по шаблону, и видит в шаблоне позитивную опережающую проверку

Далее у нас выражение (\d{3})+ и квантификатор принимает значения 1 и 2 соответственно для каждой испытуваемой позиции.
Движок продолжает идти по шаблону. Видит в шаблоне подшаблон негативной опережающей проверки (?!\d). Это значит,что в найденной позиции на три и на шесть символов вправо от исходной испытуваемой позиции
далее не должна стоять цифра.

А так как стоит флаг глобального поиска, то весь вышеописанный алгоритм действий повторяется, но только уже не с первого, а со второго символа. И так много раз. И в определенные моменты будут найдены две позиции на которые функция replace поставит запятые.
Добавлю,что можно (\d{3}) записать в функции как (?:\d{3})в целях повышения эффективности поиска.
1
Svsh2015
132 / 108 / 22
Регистрация: 23.06.2015
Сообщений: 338
12.02.2018, 20:38 #3
user7845, К #1 и#2 могу добавить следующее, поскольку описание процесса в #2 очень краткое:
Модификатор(мнимый символ) \b соответствует границе слова, то есть соответствует позиции с одной стороны которой находится символ слова(буква,цифра или знак подчёркивания),а с другой стороны такого символа нет.
Мнимый символ \B соответствует позиции внутри слова, то есть с одной и другой стороны от неё находится символ слова(буква,цифра или знак подчёркивания).
В регулярках языков Java Script, Perl,PHP задействован алгоритм поиска –НКА(недетерминированный конечный автомат(алгоритм управляется шаблоном), при этом действует принцип возвратов: в случае локальной неудачи поиска совпадения, происходит откат к предыдущему минимальному подшаблону, имеющему переменный квантификатор.
Механизм поиска совпадения ( движок) хранит текущие позиции в тексте и шаблоне.
Поиск заканчивается удачей, когда текущая позиция в шаблоне выходит за пределы шаблона.
В примере текста 12345678 для данной темы не так и много итераций:
1. Процесс сопоставления текста шаблону в нашем случае начинается в тексте с позиции между 1 и 2, ей соответствует подшаблон(мнимый символ ) \B.
Далее жадный квантификатор + выбирает максимальное значение 2,чтобы оставаться в цифровом блоке.
Соответственно мы смещается вправо на 6 позиций, то есть проверка на наличие цифры справа (опережающая негативная проверка)правее осуществляется в позиции между 7 и 8.
Проверка заканчивается локальной неудачей.
2. В соответствии с принципом возвратов происходит откат к предыдущему минимальному подшаблону, то есть к индексу квантификатора 1,то есть мы смещаемся на 3 позиции от исходной между 1 и 2,негативная опережающая проверкана наличие цифры (далее проверка) происходит в позиции между 4 и 5 .
Опять локальная неудача, цифра имеется.
3. В соответствии с принципом возвратов происходит откат к предыдущему минимальному подшаблону \B и в тексте мы смещается на одну позицию между 2 и 3
Опять жадный квантификатор + выбирает максимальное значение 2,чтобы оставаться в цифровом блоке( 3 не подойдёт).
Опять мы смещается на (3*2=)6 позиций вправо.
Проверка на наличие цифры приносит удачу, то есть позиция между 2 и 3 подходит для вставки запятой функцией Replace.
4. И на этом все бы закончилось, но специальный режим поиска с индефикатором g(global) производит откат к предыдущему минимальному подшаблону,имеющему переменный квантификатор , то есть к максимальному индексу жадного квантификатора 1(2 уже не подойдёт, чтобы остаться в цифровом блоке),а позицию в тексте увеличивается на 1 между 3 и 4.
Проверка будет осуществляться между 6 и 7 и локальная неудача, опять откат, позицию в тексте увеличивается на 1-мы между 4 и 5 проверка будет между 7 и 8, и локальная неудача вновь, далее откат,
А позицию в тексте увеличивается на 1 между 5 и 6,проверка с индексом квантификатора 1 даёт удачу
Позиция в тексте между 5 и 6 подошла для вставки запятой функцией Replace.
И так нйдены две искомые позиции.
Добавлю ,что в конце #2 в предпредпредпоследнем предложении имеется опечатка.Надо так изменить :
Весь вышеописанных алгоритм повторяется, но только не с позиции между первым и вторым символом,а с позиции между вторым и тетьим символом.
0
volodin661
1750 / 860 / 148
Регистрация: 10.12.2013
Сообщений: 2,949
13.02.2018, 08:24 #4
для простоты восприятия можно упростить regex до
/(?=(\d{3})+$)/

и для подопытной строки "12345678" всё отработает идентично --> 12,345,678

но вот если число символов в строке будет кратно 3, появится ненужная нам запятая в нулевой позиции
( ,123,456,789 ). Таким образом, метасимвол \B в начале нужен для исключения таких случаев.

оставшаяся часть (?=(\d{3})+$) заставит механизм regexp найти найти такую позицию в строке для вставки запятой,
при которой группы из трех цифр кратно уложатся в строку.

Тем, кто умеет управляться с Perl,
могу посоветовать использовать фантастический модуль Дамиана Конвея ( Damian Conway ) Regexp::Debugger,
который наглядно в весёлых картинках покажет, как работает данный regex.

1.pl
Perl
1
2
3
4
5
6
7
use Regexp::Debugger;
 
$str = "12345678";
 
$str =~ s /\B(?=(\d{3})+$)/,/g;
 
print $str;
Bash
1
perl 1.pl
0
Svsh2015
132 / 108 / 22
Регистрация: 23.06.2015
Сообщений: 338
13.02.2018, 10:37 #5
user7845, в Perl очень иллюстративен вариант с позитивной ретроспективной проверкой

Код
$t=~s!(?<=\d)(?=(?:\d{3})+(?!\d))!,!g;
К сожалению в Java Script нет ретроспективных проверок

Для Java Script можно приспособить такой вариант:
Код
$t=~s!(\d)(?=(?:\d{3})+(?!\d))!$1,!g;
0
user7845
5 / 5 / 0
Регистрация: 07.06.2012
Сообщений: 98
13.02.2018, 22:10  [ТС] #6
Спасибо всем за ответы. Есть над чем поразмыслить
0
volodin661
1750 / 860 / 148
Регистрация: 10.12.2013
Сообщений: 2,949
16.02.2018, 08:55 #7

Не по теме:

Может кому будет любопытно:

Larry Wall ( создатель языка Perl и, соответственно, автор синтаксиса регулярных выражений Perl)
придумал другой синтаксис - совсем не PCRE,
который на данный момент реализован в языке Perl6

вот полностью рабочий пример:

Perl
1
2
3
4
5
my \РАЗДЕЛИТЕЛЬ = ',';
 
'12345678'
 .subst( / <?after \d> #`(разделитель будет вставлен здесь) <?before [\d**3]+ $> /, РАЗДЕЛИТЕЛЬ, :global)
 .print
как видно, у синтаксиса вполне себе человеческий вид.



Добавлено через 37 минут

Не по теме:

вот так совсем по-человечески:

Perl
1
2
3
4
5
6
7
my \РАЗДЕЛИТЕЛЬ = ',';
my token ЦИФРА  { \d };
my token ГРУППЫ-ПО-ТРИ-ЦИФРЫ { [<ЦИФРА> ** 3]+ $};
 
«12345678»
 .subst( / <?after <ЦИФРА> > #`(разделитель будет вставлен здесь) <?before <ГРУППЫ-ПО-ТРИ-ЦИФРЫ> >/, РАЗДЕЛИТЕЛЬ, :g)
 .say

1
evikza
390 / 242 / 160
Регистрация: 20.05.2015
Сообщений: 511
17.02.2018, 23:06 #8
user7845, возможно совсем не в тему, но вдруг кому-то пригодится.

В JS есть нативный метод для работы с числами — Intl.NumberFormat.

Javascript
1
2
3
var numberFormatter = new Intl.NumberFormat('en-US');
 
console.log(numberFormatter.format(12345678)); // -> 12,345,678
Документация и поддержка браузерами + полифил для браузеров.
1
user7845
5 / 5 / 0
Регистрация: 07.06.2012
Сообщений: 98
19.02.2018, 10:01  [ТС] #9
Прикольный метод. Не знал о его существовании. Мне пригодится.
0
19.02.2018, 10:01
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
19.02.2018, 10:01

Конец и начало строки для регулярного выражения
Доброго всем дня ) Я не селен в регулярка, прошу помощи Написал вот такое...

Построение регулярного выражения
Добрый вечер форумчане. Сейчас прохожу тему про &quot;регулярные выражения&quot;. Тема...

Переделать с регулярного выражения в простую функцию Java Script
&lt;!doctype html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;Lab 7&lt;/title&gt; &lt;meta charset=&quot;utf-8&quot;&gt; ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru