Вы, кажется, не понимают, как что регулярные выражения работ. Позвольте мне разбить его для вас:
(?= 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})$)
Спасибо за подробное объяснение. Еще один вопрос. Когда вы используете $, это действительно поиск отскакивания с этой точки или это какие-то повторы слева направо? Например, в этом простом тесте: https://regex101.com/r/eE6zK7/1 говорится, что совпадение было найдено после 20 шагов. – Rafael
@Rafael: '$' это просто привязка конца строки, это не влияет на «направление» регулярного выражения. Если вы нажмете «debugger» на regex101, вы увидите, как он будет соответствовать шаг за шагом. –