Как работает \ G в .split? - PullRequest
0 голосов
/ 16 мая 2018

Мне нравится выполнять игру в коде в Java (хотя Java слишком многословен, чтобы быть конкурентоспособным), что решает определенную задачу в минимально возможном количестве байтов.В одном из моих ответов у меня был следующий фрагмент кода:

for(var p:"A4;B8;CU;EM;EW;E3;G6;G9;I1;L7;NZ;O0;R2;S5".split(";"))

, который в основном циклически перебирает 2-символьные строки после того, как мы преобразовали его в массив String с .split.Кто-то предложил мне использовать это вместо этого, чтобы сэкономить 4 байта:

for(var p:"A4B8CUEMEWE3G6G9I1L7NZO0R2S5".split("(?<=\\G..)"))

Функциональность все та же.Он перебирает 2-символьные строки.

Однако никто из нас не был уверен на 100%, как это работает, отсюда и этот вопрос.


Что я знаю:

Я знаю, .split("(?<= ... )") используется для разделения, но сохраняет конечный разделитель.
Существует также способ сохранить ведущий разделитель или разделитель как отдельный элемент:

"a;b;c;d".split("(?<=;)")            // Results in ["a;", "b;", "c;", "d"]
"a;b;c;d".split("(?=;)")             // Results in ["a", ";b", ";c", ";d"]
"a;b;c;d".split("((?<=;)|(?=;))")    // Results in ["a", ";", "b", ";", "c", ";", "d"]

Я знаю, \G используется для остановки после обнаружения несоответствия.
РЕДАКТИРОВАТЬ: \G используется для указания позиции, где закончилось последнее совпадение (илиначало строки для первого запуска). Исправленное определение благодаря @SebastianProske.

int count = 0;
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("match,");
java.util.regex.Matcher matcher = pattern.matcher("match,match,match,blabla,match,match,");
while(matcher.find())
  count++;
System.out.println(count); // Results in 5

count = 0;
pattern = java.util.regex.Pattern.compile("\\Gmatch,");
matcher = pattern.matcher("match,match,match,blabla,match,match,");
while(matcher.find())
  count++;
System.out.println(count); // Results in 3

Но как именно .split("(?<=\\G..)") работает точно при использовании \G внутри разбиения?
А почему .split("(?=\\G..)") не работает?

Здесь ссылка "Попробуйте онлайн" для всех описанных выше фрагментов кода, чтобы увидеть их в действии.

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Прежде всего, \G определение: это якорь, который соответствует началу строки или концу предыдущего соответствия.Это позиция.Он не потребляет символ и не меняет позицию курсора.Алан Мур ранее в ответе писал, что такое поведение \G внутри lookbehinds зависит от движка.Это разделит на желаемую длину в Java, но не даст тот же результат в PCRE.

Так как же работает \G в (?<=\G..)?Посмотрите на пошаговую демонстрацию ниже, где точка и \G совпадают:

 ↓A4
\G..↓B8
   \G..↓CU
      \G..
       .
       .

\G соответствует началу входной строки, затем точки соответствуют A и 4 по порядку.Двигатель продолжает движение и останавливается прямо между 8 и C.Вот совпадения вида:

A   4   B  8
     \G .  . (?<=\G..)

Где совпадения \G - это совпадение, в котором предыдущие точки закончили сопоставление, т.е. положение сразу после 4 и до B.Этот процесс продолжается до конца входной строки.Он разбивает строку на 2 единицы данных (здесь можно смело использовать символ).Он не должен работать с многострочными входными строками и, если он делает это, частично разделяется, так как точка . не соответствует символу новой строки или вообще не разделяется, так как \G не соответствует началу строки(только начало входной строки).

И почему .split("(?=\\G..)") не работает?

Из-за природы предвидения - который смотрит вперед - нет никаких возможностей дляэто встретиться, где предыдущий матч закончился.Он просто продолжает идти до самого конца.

0 голосов
/ 16 мая 2018

как .split("(?<=\\G..)") работает

(?<=X) - положительный вид сзади нулевой ширины для X. \G - конец предыдущего совпадения (не какая-либо инструкция остановки) или начало ввода, и, конечно, .. - это два отдельных символа. Таким образом, (?<=\G..) - это вид сзади нулевой ширины для конца предыдущего совпадения плюс два символа. Так как это split, и мы описываем разделитель , то есть утверждение целиком означает, что утверждение нулевой ширины означает, что мы используем его только для определения, где нужно разбить строку, а не для фактического использования каких-либо символов.

Итак, давайте пройдемся по ABCDEF:

  1. \G соответствует началу ввода, а .. соответствует AB, поэтому (?<=\G..) находит пробел нулевой ширины между AB и CD, потому что это взгляд назад: то есть первая точка в котором перед стоит \G.. до курсора регулярного выражения - точка между AB и CD. Таким образом, разделить между AB и CD.
  2. \G отмечает местоположение сразу после AB, поэтому (?<=\G..) находит пробел нулевой ширины между CD и EF, потому что при перемещении курсора регулярного выражения это первое место, где \G.. соответствует : \G соответствует местоположению между AB и CD и .. соответствует CD. Таким образом, разделить между CD и EF.
  3. То же самое: \G отмечает местоположение сразу после CD, поэтому (?<=\G..) находит пробел нулевой ширины между EF и концом ввода. Так что разделите между EF и концом ввода.
  4. Создайте массив со всеми совпадениями, кроме пустого в конце (потому что это split с неявным length = 0, который отбрасывает пустые строки в конце).

Результат { "AB", "CD", "EF" }.

А почему .split("(?=\\G..)") не работает?

Потому что (?=X) - позитивный взгляд впереди . Конец предыдущего матча никогда не будет на впереди от курсора регулярного выражения. Это может быть только позади it.

...