codingBat repeatEnd используя регулярное выражение
Я пытаюсь понять регулярные выражения настолько, насколько я могу, поэтому я придумал это основанное на регулярных выражениях решение для codingbat.comrepeatEnd
:
Если задана строка и int N, вернуть строку, состоящую из N повторений последних N символов строки. Вы можете предположить, что N находится между 0 и длиной строки включительно.
public String repeatEnd(String str, int N) {
return str.replaceAll(
".(?!.{N})(?=.*(?<=(.{N})))|."
.replace("N", Integer.toString(N)),
"$1"
);
}
Пояснения по его частям:
.(?!.{N})
: утверждает, что соответствующий символ является одним из последних N символов, следя за тем, чтобы за ним не было N символов.(?=.*(?<=(.{N})))
: в этом случае используйте lookforward, чтобы сначала пройти весь путь до конца строки, затем вложенный lookbehind, чтобы захватить последние N символов в\1
, Обратите внимание, что это утверждение всегда будет верным.|.
: если первое утверждение не выполнено (т. е. впереди не менее N символов), то в любом случае соответствует символу;\1
будет пустым.В любом случае символ всегда совпадает; заменить его на
\1
,
Мои вопросы:
- Является ли эта техника вложенных утверждений действительной? (то есть, оглядываясь назад во время предвидения?)
- Есть ли более простое решение на основе регулярных выражений?
Бонусный вопрос
Делать repeatBegin
(как определено аналогично).
У меня правда есть проблемы с этим!
3 ответа
Хороший! Я не вижу способа значительно улучшить это регулярное выражение, хотя я бы реорганизовал его, чтобы избежать ненужного использования негативной логики:
".(?=.{N})|.(?=.*(?<=(.{N})))"
Таким образом, вторая альтернатива никогда не вводится до тех пор, пока вы не достигнете последних N символов, что, я думаю, немного прояснит цель.
Я никогда не видел упоминаний, в которых говорилось бы, что все в порядке, но, как и Барт, я не понимаю, почему это не так. Я иногда использую lookaheads внутри lookbehinds, чтобы обойти ограничения выражений lookbehind переменной длины.
РЕДАКТИРОВАТЬ: я только что понял, что могу немного упростить регулярное выражение, поместив чередование внутри прогнозирования:
".(?=.{N}|.*(?<=(.{N})))"
Кстати, вы рассматривали возможность использования format()
построить регулярное выражение вместо replace()
?
return str.replaceAll(
String.format(".(?=.{%1$d}|.*(?<=(.{%1$d})))", N),
"$1"
);
Оу, это какое-то страшное регулярное вуду!:)
- Является ли эта техника вложенных утверждений действительной? (то есть, оглядываясь назад во время предвидения?)
Да, это совершенно справедливо в большинстве реализаций PCRE, о которых я знаю.
- Есть ли более простое решение на основе регулярных выражений?
Я не тратил на это слишком много времени, но не сразу понял, как это можно упростить или сократить с помощью одной замены регулярных выражений.
Есть ли более простое решение на основе регулярных выражений?
Это заняло у меня некоторое время, но в итоге мне удалось упростить регулярное выражение:
"(?=.{0,N}$(?<=(.{N}))).|." // repeatEnd
-or-
".(?<=^(?=(.{N})).{0,N})|." // repeatBegin
Как и ответ Алана Мура, это удаляет отрицательное утверждение, но даже не заменяет его положительным, так что теперь оно имеет только 2 утверждения вместо 3.
Мне также нравится тот факт, что случай "еще" просто .
, Я предпочитаю помещать основную часть моего регулярного выражения в "рабочую" сторону чередования и сохранять "нерабочую" сторону как можно более простой (обычно простой .
или же .*
).