2016-05-08 3 views
0

Я хочу изучить немного больше о регулярных выражениях. Add a space on a string but counting right to leftГруппировка в регулярных выражениях

В результате этого регулярного выражения

preg_replace("/(?=(.{3})*(.{4})$)/", "-", "1231231234"); 

является: 123-123-1234

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

Почему это (PHP)

preg_replace("/(?=(.{3})*(.{4})(.{4})$)/", "-", "1212312312345678"); 

и это:

preg_replace("/(?=(.{3})*(.{4}){2}$)/", "-", "1212312312345678"); 

оба дают мне большую группу 8 символов как выход

12-123-123-12345678 

я, вероятно, ожидал, что результат на второй случай {2}, но не в первом случае.

Ожидаемый результат я намеревался был:...

12-123-123-1234-5678 

1) Какова логика на ({4}) ({4}) = ({8}) вместо того, чтобы 2 разных события?

2) Что было бы правильной группировкой?

ответ

1

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

(?=   lookahead assertion: the following pattern must match, but 
      will not consume any of the text. 
    (.{3})* matches a series of 3 characters, any number of times. In 
      other words, this consumes characters in multiples of 3. 
    (.{4})$ makes sure there are exactly 4 characters left. 
) 

Эта модель производит пустого матч в любом месте, где вы хотите вставить тир -.Вот почему preg_replace("/(?=(.{3})*(.{4})$)/", "-", "1231231234");вставки тире в правильных местах - замена пустой строки такая же, как и вставка. Давайте посмотрим на этот шаг, шаг за, используя текст 31231234 в качестве примера:

  remaining text  remaining pattern  what happens 
step 0: 31231234   (.{3})*(.{4})$   (.{3})* matches one time 
step 1: 31234    (.{3})*(.{4})$   (.{3})* matches again 
step 2: 34     (.{3})*(.{4})$   (.{3})* fails to match another time 
step 3: 34     (.{4})$    (.{4}) fails to match -> backtrack 
step 5: 31234    (.{4})$    (.{4}) fails to match -> pattern failed to 
                match, no dash will be inserted. 

После того, как картина не соответствовала в позиции 0 в тексте, будет проверяться снова в положении 1 (оставшийся текст 1231234):

  remaining text  remaining pattern  what happens 
step 0: 1231234   (.{3})*(.{4})$   (.{3})* matches one time 
step 1: 1234    (.{3})*(.{4})$   (.{3})* matches again 
step 2: 4     (.{3})*(.{4})$   (.{3})* fails to match another time 
step 3: 4     (.{4})$    (.{4})$ matches -> dash will be inserted 
                here, giving "3-1231234" 

То же самое происходит и снова 3-х символов позже, давая конечный результат 3-123-1234. Другими словами, группа (.{4})$ указывает, что никакие тире не должны вставляться в последние 4 символа текста. Поглощая последние 4 символа, он не позволяет совпадению шаблона, если осталось менее 4 символов. Вот почему и (.{4})(.{4})$, и (.{4}){2}$ создают блок из 8 символов - шаблон не может совпадать, если осталось менее 8 символов.

Для того, чтобы вставить еще один дефис в последние 8 символов, вы должны использовать две группы из 4-х символов .{4} и сделать одну из них дополнительно:

(?=((.{3})*.{4})?(.{4})$) 
+0

Спасибо за подробное объяснение. Еще один вопрос. Когда вы используете $, это действительно поиск отскакивания с этой точки или это какие-то повторы слева направо? Например, в этом простом тесте: https://regex101.com/r/eE6zK7/1 говорится, что совпадение было найдено после 20 шагов. – Rafael

+0

@Rafael: '$' это просто привязка конца строки, это не влияет на «направление» регулярного выражения. Если вы нажмете «debugger» на regex101, вы увидите, как он будет соответствовать шаг за шагом. –

2

Обратите внимание, что в этом случае вы используете lookaheads. В отличие от обычного соответствия, они фактически не потребляют то, что они соответствуют.

Таким образом, в первом примере, есть 2 нулевой ширина матчи, первая после первого 123, поэтому опережения матчей за 1231234, вторые после второго 123, где опережение соответствует 1234. Возможно, вы захотите использовать один из онлайн-regex-тестеров, чтобы увидеть, что на самом деле соответствует, мой выбор будет regex101.com.

Итак, для вашего примера вы должны сделать, чтобы lookahead также соответствовал последним 4 цифрам (и только им), одним из способов достижения этого было бы (?=((.{3})*(.{4}))?(.{4})$), что сделало бы первую часть опциональной.

См. Здесь, regex101.

+0

Спасибо, да я быть играет с regex101: о), да, одна вещь, которую я собираюсь понять больше, - это то, что один шаблон совпадает и с другими шаблонами. Ty. – Rafael

1

(?=(.{3})*(.{4}){2}$) матчей каждой последовательности символов 3XN с 2x4 = 8 символов в конце, где N> = 0.

Для того, чтобы соответствовать каждому символу 4xN от конца, где 1 = N < < = 2, или каждый 3XN последовательность символов 8 символов в конце, где N> = 1, используйте следующее:

preg_replace("/(?=(.{4}){1,2}$)|(?=(.{3})+.{8}$)/", "-", "1212312312345678"); 
+0

Iwteresting. На данный момент я не думаю, что буду использовать вертикальную панель, но это хороший пример. – Rafael

Смежные вопросы