Найти несколько совпадений регулярных выражений в Java, запрещающих несоответствия - PullRequest
0 голосов
/ 20 января 2020

У меня есть Java Pattern, например \s+(foo|bar), чтобы найти все совпадения foo или bar после пробела. Используя соответствующую группу, я могу извлечь фактический сопоставленный текст.

Pattern pattern=Pattern.compile("\\s+(foo|bar)");
Matcher matcher = pattern.match(someText);
while(matcher.find()) {
  String value = matcher.group(1);
  ...
}

Это работает для строк типа foo foo bar (обратите внимание на предыдущий пробел), но оно также соответствует вещам типа foo foo bad. Как я могу либо запретить сопоставителю сопоставлять последующие серии символов, которые не совпадают, либо обнаружить, что символы были пропущены или в них больше не осталось символов? Другими словами, я ожидаю, что вся сопоставляемая строка будет последовательностью последующих строк, соответствующих шаблону. Как я могу гарантировать это?

Смысл в том, чтобы продолжать проходить через поиск строк. Я мог бы легко разбить строку и затем выполнить дополнительные сравнения, но я не хочу накладных расходов на несколько проходов регулярных выражений, создание массивов / списков и т. Д. c.

Ответы [ 2 ]

2 голосов
/ 20 января 2020

Префикс регулярного выражения с \G. Джавадо c из Pattern говорит:

\G - Конец предыдущего матча

Конечно, на первом match, «конец предыдущего совпадения» - это начало ввода.

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

Пример

public static void main(String[] args) {
    test("abc");
    test(" foo foo bar");
    test(" foo foo bad");
    test(" foo bad foo");
}
static void test(String input) {
    System.out.println("'" + input + "'");
    int lastEnd = 0;
    Matcher m = Pattern.compile("\\G\\s+(foo|bar)").matcher(input);
    while (m.find()) {
        System.out.printf("  g0='%s' (%d-%d), g1='%s' (%d-%d)%n",
                          m.group(), m.start(), m.end(),
                          m.group(1), m.start(1), m.end(1));
        lastEnd = m.end();
    }
    if (lastEnd == input.length())
        System.out.println("  OK");
    else
        System.out.println("  Incomplete: Last match ended at " + lastEnd);
}

Output

'abc'
  Incomplete: Last match ended at 0
' foo foo bar'
  g0=' foo' (0-4), g1='foo' (1-4)
  g0=' foo' (4-8), g1='foo' (5-8)
  g0=' bar' (8-12), g1='bar' (9-12)
  OK
' foo foo bad'
  g0=' foo' (0-4), g1='foo' (1-4)
  g0=' foo' (4-8), g1='foo' (5-8)
  Incomplete: Last match ended at 8
' foo bad foo'
  g0=' foo' (0-4), g1='foo' (1-4)
  Incomplete: Last match ended at 4

Для сравнения, без \G в регулярном выражении, вывод этого кода будет:

'abc'
  Incomplete: Last match ended at 0
' foo foo bar'
  g0=' foo' (0-4), g1='foo' (1-4)
  g0=' foo' (4-8), g1='foo' (5-8)
  g0=' bar' (8-12), g1='bar' (9-12)
  OK
' foo foo bad'
  g0=' foo' (0-4), g1='foo' (1-4)
  g0=' foo' (4-8), g1='foo' (5-8)
  Incomplete: Last match ended at 8
' foo bad foo'
  g0=' foo' (0-4), g1='foo' (1-4)
  g0=' foo' (8-12), g1='foo' (9-12)
  OK

Как видите, последний пример не смог бы обнаружить этот текст bad ва пропущен.

0 голосов
/ 20 января 2020

Решение, которое требует дополнительного match, состоит в том, чтобы сначала попытаться сопоставить входные данные со следующим регулярным выражением:

^(\s+(foo|bar))+$

Затем вы можете выполнить повторные поиски:

import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class Test
{
    public static void main(String[] args) {
        String[] tests =  {
            " foo foo bar",
            " foo foo x foo bar"
        };
        Pattern pattern1 = Pattern.compile("(\\s+(foo|bar))+");
        Pattern pattern2 = Pattern.compile("\\s+(foo|bar)");
        for (int i = 0; i < tests.length; i++) {
            String test = tests[i];
            Matcher m1 = pattern1.matcher(test);
            if (m1.matches()) {
                System.out.println("Matches against: '" + test + "'");
                Matcher m2 = pattern2.matcher(test);
                while (m2.find()) {
                    System.out.println("\t'" + m2.group() + "'");
                }
            }
        }
    }
}

Печать:

Matches against: ' foo foo bar'
        ' foo'
        ' foo'
        ' bar'

Если весь ввод не должен совпадать, то мы используем регулярное выражение, чтобы найти префикс строки, которая:

^(\s+(foo|bar))+

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

А затем:

import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class Test
{
    public static void main(String[] args) {
        String[] tests =  {
            " foo foo bar",
            " foo foo x foo bar"
        };
        Pattern pattern1 = Pattern.compile("^(\\s+(foo|bar))+");
        Pattern pattern2 = Pattern.compile("\\s+(foo|bar)");
        for (int i = 0; i < tests.length; i++) {
            String test = tests[i];
            Matcher m1 = pattern1.matcher(test);
            if (m1.find()) {
                String s = m1.group();
                System.out.println("Matches against: '" + s + "'");
                Matcher m2 = pattern2.matcher(s);
                while (m2.find()) {
                    System.out.println("\t'" + m2.group() + "'");
                }
            }
        }
    }
}

Отпечатки:

Matches against: ' foo foo bar'
        ' foo'
        ' foo'
        ' bar'
Matches against: ' foo foo'
        ' foo'
        ' foo'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...