Как захватить номер группы из каждой группы в группе повторного захвата - PullRequest
2 голосов
/ 06 апреля 2020

Мое регулярное выражение - что-то вроде **(A)(([+-]\d{1,2}[YMD])*)**, которое соответствует ожидаемому, например, A + 3M, A-3Y + 5M + 3D et c ..

Но я хочу захватить все группы этот подшаблон **([+-]\d{1,2}[YMD])*** Для следующего примера A-3M + 2D я вижу только 4 групп. A-3M+2D (group 0), A(group 1), -3M+2D (group 2), +2D (group 3)

Есть ли способ получить **-3M** в виде отдельной группы?

1 Ответ

1 голос
/ 06 апреля 2020

Повторные группы захвата обычно захватывают только последнюю итерацию . Это верно как для Kotlin, так и для Java, поскольку в языках нет никакого метода, который бы отслеживал каждый стек групп захвата.

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

Для текущего сценария вы можете использовать

val text = "A-3M+2D" 
if (text.matches("""A(?:[+-]\d{1,2}[YMD])*""".toRegex())) {
  val results =  text.split("(?=[-+])".toRegex())
  println(results)
}
// => [A, -3M, +2D]

См. Демоверсию Kotlin

Здесь

  • text.matches("""A(?:[+-]\d{1,2}[YMD])*""".toRegex()) гарантирует, что вся строка соответствует A, а затем 0 или более вхождений + или -, 1 или 2 цифры с последующими Y, M или D
  • .split("(?=[-+])".toRegex()) разделяет текст с пустой строкой прямо перед - или +.

Детали шаблона

  • ^ - неявно в .matches() - начало строки
  • A - A подстрока
  • (?: - начало группы без захвата :
    • [+-] - класс символов согласование + или -
    • \d{1,2} - от одной до двух цифр
    • [YMD] - класс символов, соответствующий Y или M или D
  • )* - конец группы без захвата, повторить 0 или более раз (из-за * квантификатор)
  • \z - подразумевается в matches() - конец строки.

При разбиении нам просто нужно найти местоположения до - или +, поэтому мы используем положительное значение lookahead , (?=[-+]), соответствующее позиция, за которой сразу следует + или -. Это непотребляющий шаблон, сопоставленные + или - не добавляются к значению соответствия.

Другой подход с одним регулярным выражением

You может также использовать регулярное выражение \G для проверки формата строки сначала в начале строки и только начинать сопоставлять последовательные подстроки, если эта проверка прошла успешно:

val regex = """(?:\G(?!^)[+-]|^(?=A(?:[+-]\d{1,2}[YMD])*$))[^-+]+""".toRegex()
println(regex.findAll("A-3M+2D").map{it.value}.toList())
// => [A, -3M, +2D]

См. другой Kotlin demo и regex demo .

Подробности

  • (?:\G(?!^)[+-]|^(?=A(?:[+-]\d{1,2}[YMD])*$)) - либо конец предыдущего успешного совпадение и затем + или - (см. \G(?!^)[+-]) или (|) начало строки, за которой следует A, а затем 0 или более вхождений + / -, 1 или 2 цифры и затем Y, M или D до конца строки (см. ^(?=A(?:[+-]\d{1,2}[YMD])*$))
  • [^-+]+ - 1 или более символов, отличных от - и +. Нам не нужно быть здесь слишком осторожными, так как оглядывающая сторона сделала тяжелую работу в начале строки.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...