Использование класса Java Matcher в регулярных выражениях

Класс Java Matcher (java.util.regex.Matcher) используется для поиска в тексте нескольких вхождений регулярного выражения. Вы также можете использовать его для поиска одного и того же выражения в разных текстах.

Пример

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

public class MatcherExample {

    public static void main(String[] args) {

        String text    =
            "This is the text to be searched " +
            "for occurrences of the http:// pattern.";

        String patternString = ".*http://.*";

        Pattern pattern = Pattern.compile(patternString);

        Matcher matcher = pattern.matcher(text);
        boolean matches = matcher.matches();
    }
}

Сначала экземпляр Pattern создается из регулярного выражения, а из экземпляра Pattern создается экземпляр Matcher. Затем метод matches() вызывается для экземпляра Matcher. Он возвращает true, если регулярное выражение соответствует тексту, и false, если нет.

Создание

Создание выполняется с помощью метода matcher() в классе Pattern:

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

public class CreateMatcherExample {

    public static void main(String[] args) {

        String text    =
            "This is the text to be searched " +
            "for occurrences of the http:// pattern.";

        String patternString = ".*http://.*";

        Pattern pattern = Pattern.compile(patternString);

        Matcher matcher = pattern.matcher(text);
    }
}

В конце этого примера переменная matcher будет содержать экземпляр Matcher, который можно использовать для сопоставления регулярного выражения Java, используемого для его создания, с другим вводом текста.

matches()

Метод matches() в классе Matcher сопоставляет регулярное выражение со всем текстом, переданным методу Pattern.matcher() при создании Matcher:

String patternString = ".*http://.*";
Pattern pattern = Pattern.compile(patternString);

boolean matches = matcher.matches();

Если выражение соответствует всему тексту, то метод matches() возвращает true. Если нет, возвращает false.

Вы не можете использовать метод matches() для поиска нескольких вхождений выражения в тексте. Для этого вам нужно использовать методы find(), start() и end().

lookingAt()

Метод работает подобно методу matches() с одним существенным отличием. Он сопоставляет регулярное выражение только с началом текста, тогда как matches() сопоставляет с целым текстом. Другими словами, если выражение соответствует началу текста, но не всему тексту, lookAt() вернет true, тогда как matches() вернет false:

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

public class CreateMatcherExample {

    public static void main(String[] args) {

        String text    =
                "This is the text to be searched " +
                "for occurrences of the http:// pattern.";

        String patternString = "This is the";

        Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(text);

        System.out.println("lookingAt = " + matcher.lookingAt());
        System.out.println("matches   = " + matcher.matches());
    }
}

Этот пример сопоставляет регулярное выражение «This is the» как с началом текста, так и со всем текстом. Сопоставление с началом текста (lookingAt()) вернет true.

Сопоставление со всем текстом (matches()) вернет false, потому что текст содержит больше символов, чем выражение, а оно говорит, что текст должен точно соответствовать тексту «This», без лишних символов до или после.

find() + start() + end()

Метод find() ищет вхождения выражений в тексте, переданном методу Pattern.matcher(text) при создании Matcher. Если в тексте можно найти несколько совпадений, метод найдет первое, а затем при каждом последующем вызове будет переходить к следующему совпадению.

Методы start() и end() передадут индексы в текст, где найденное совпадение начинается и заканчивается. На самом деле end() возвращает индекс символа конца соответствующего раздела. Таким образом, вы можете использовать возвращаемые значения start() и end() внутри вызова String.substring().

Вот пример:

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

public class MatcherFindStartEndExample {

    public static void main(String[] args) {

        String text    =
                "This is the text which is to be searched " +
                "for occurrences of the word 'is'.";

        String patternString = "is";

        Pattern pattern = Pattern.compile(patternString);
        Matcher matcher = pattern.matcher(text);

        int count = 0;
        while(matcher.find()) {
            count++;
            System.out.println("found: " + count + " : "
                    + matcher.start() + " - " + matcher.end());
        }
    }
}

Этот пример найдет шаблон «is» четыре раза в искомой строке. Вывод будет напечатан так:

found: 1 : 2 - 4
found: 2 : 5 - 7
found: 3 : 23 - 25
found: 4 : 70 - 72

reset()

Метод reset() сбрасывает внутреннее состояние соответствия в Matcher. В случае, если вы начали сопоставлять вхождения в строке с помощью метода find(), будет внутренне сохраняться состояние о том, как далеко он провел поиск во входном тексте. При вызове reset() сопоставление снова начнется с начала текста.

Существует также метод сброса (CharSequence). Он сбрасывает Matcher и выполняет поиск через CharSequence, переданный в качестве параметра, вместо CharSequence, с которой изначально был создан Matcher.

group()

Представьте, что вы ищете в тексте URL-адреса, и вы хотели бы извлечь найденные URL-адреса из текста. Конечно, вы можете сделать это с помощью методов start() и end(), но это проще сделать с помощью групповых функций.

Группы отмечены круглыми скобками:

(John)

Это выражение соответствует тексту John. Скобки не являются частью текста, который соответствует. Скобки отмечают группу. Когда в тексте найдено совпадение, вы можете получить доступ к части выражения внутри группы.

Доступ к группе осуществляется с помощью метода group(int groupNo). Выражение может иметь более одной группы. Таким образом, каждая группа помечается отдельным набором скобок. Чтобы получить доступ к тексту, который соответствует части выражения в определенной группе, передайте номер группы методу group(int groupNo).

Группа с номером 0 всегда является целым выражением. Чтобы получить доступ к группе, отмеченной круглыми скобками, вы должны начать с номера группы 1.

Вот пример:

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

public class MatcherGroupExample {

    public static void main(String[] args) {

        String text    =
                  "John writes about this, and John writes about that," +
                          " and John writes about everything. "
                ;

        String patternString1 = "(John)";

        Pattern pattern = Pattern.compile(patternString1);
        Matcher matcher = pattern.matcher(text);

        while(matcher.find()) {
            System.out.println("found: " + matcher.group(1));
        }
    }
}

Этот пример ищет в тексте вхождения слова John. Для каждого найденного совпадения извлекается группа № 1, которая соответствует группе, отмеченной круглыми скобками. Результат примера:

found: John
found: John
found: John

Несколько групп

Как упоминалось ранее, регулярное выражение может иметь несколько групп:

(John)(.+?)

Это выражение соответствует тексту «John», за которым следует пробел, а затем один или несколько символов. Вы не можете видеть это в примере выше, но после последней группы есть пробел.

Это выражение содержит несколько символов со специальным значением.

  • . означает «любой символ».
  • Знак + означает «один или несколько раз» и относится к. (Любой символ, один или несколько раз).
  • ? означает «сопоставить как можно меньшее количество символов».

Вот полный пример кода:

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

public class MatcherGroupExample {

    public static void main(String[] args) {

        String text    =
                  "John writes about this, and John Doe writes about that," +
                          " and John Wayne writes about everything."
                ;

        String patternString1 = "(John)(.+?) ";

        Pattern pattern = Pattern.compile(patternString1);
        Matcher matcher = pattern.matcher(text);

        while(matcher.find()) {
            System.out.println("found: " + matcher.group(1) +
                               " "       + matcher.group(2));
        }
    }
}

Обратите внимание на ссылку на две группы. Символы, соответствующие этим группам, печатаются в System.out. Вот что распечатывает пример:

found: John writes
found: John Doe
found: John Wayne

Группы внутри групп

Вот пример:

((John)(.+?))

Обратите внимание на то, как две группы из предыдущих примеров теперь вложены в большую группу. (опять же, вы не можете увидеть пробел в конце выражения, но он есть).

Когда группы вложены друг в друга, они нумеруются в зависимости от того, когда встречается левый парантез группы. Таким образом, группа 1 является большой группой. Группа 2 — это группа с выражением John внутри. Группа 3 — это группа с выражением. +? внутри. Это важно знать, когда вам нужно ссылаться на группы с помощью метода groups(int groupNo).

Вот пример, который использует вышеупомянутые вложенные группы:

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

public class MatcherGroupsExample {

    public static void main(String[] args) {

        String text    =
                  "John writes about this, and John Doe writes about that," +
                          " and John Wayne writes about everything."
                ;

        String patternString1 = "((John)(.+?)) ";

        Pattern pattern = Pattern.compile(patternString1);
        Matcher matcher = pattern.matcher(text);

        while(matcher.find()) {
            System.out.println("found:   ");
        }
    }
}

Вот вывод:

found:   
found:   
found:   

Обратите внимание, что значение, сопоставленное первой группой (внешней группой), содержит значения, сопоставленные обеими внутренними группами.

replaceAll() + replaceFirst()

Методы replaceAll() и replaceFirst() можно использовать для замены частей строки, в которой ищет Matcher. Метод replaceAll() заменяет все совпадения. ReplaceFirst() заменяет только первое совпадение.

Перед выполнением любого сопоставления Matcher сбрасывается, поэтому сопоставление начинается с начала входного текста.

Вот два примера:

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

public class MatcherReplaceExample {

    public static void main(String[] args) {

        String text    =
                  "John writes about this, and John Doe writes about that," +
                          " and John Wayne writes about everything."
                ;

        String patternString1 = "((John)(.+?)) ";

        Pattern pattern = Pattern.compile(patternString1);
        Matcher matcher = pattern.matcher(text);

        String replaceAll = matcher.replaceAll("Joe Blocks ");
        System.out.println("replaceAll   = " + replaceAll);

        String replaceFirst = matcher.replaceFirst("Joe Blocks ");
        System.out.println("replaceFirst = " + replaceFirst);
    }
}

И вот что выводит пример:

replaceAll   = Joe Blocks about this, and Joe Blocks writes about that,
    and Joe Blocks writes about everything.
replaceFirst = Joe Blocks about this, and John Doe writes about that,
    and John Wayne writes about everything.

Разрывы строк и отстранение следующей строки на самом деле не являются частью вывода. Добавлены, чтобы облегчить чтение.

Обратите внимание, что первая напечатанная строка содержит все вхождения John со словом после, замененным строкой Joe Blocks. Вторая строка заменяет только первое вхождение.

appendReplacement() + appendTail()

Методы appendReplacement() и appendTail() используются для замены строковых токенов во входном тексте и добавления результирующей строки в StringBuffer.

Когда нашли совпадение с помощью метода find(), вы можете вызвать appendReplacement(). Это приводит к тому, что символы из входного текста добавляются в StringBuffer, а соответствующий текст заменяется. Копируются только символы, начиная с конца последнего совпадения и до того момента, когда совпадающие символы будут скопированы.

Метод appendReplacement() отслеживает то, что было скопировано в StringBuffer, поэтому вы можете продолжать поиск совпадений с помощью find(), пока во входном тексте совпадений не будет найдено.

Как только будет найдено последнее совпадение, часть входного текста все равно не будет скопирована в StringBuffer. Это символы с конца последнего совпадения и до конца введенного текста. Вызывая appendTail(), вы также можете добавить эти последние символы в StringBuffer.

Вот пример:

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

public class MatcherReplaceExample {

    public static void main(String[] args) {

        String text    =
                  "John writes about this, and John Doe writes about that," +
                          " and John Wayne writes about everything."
                ;

        String patternString1 = "((John)(.+?)) ";

        Pattern      pattern      = Pattern.compile(patternString1);
        Matcher      matcher      = pattern.matcher(text);
        StringBuffer stringBuffer = new StringBuffer();

        while(matcher.find()){
            matcher.appendReplacement(stringBuffer, "Joe Blocks ");
            System.out.println(stringBuffer.toString());
        }
        matcher.appendTail(stringBuffer);

        System.out.println(stringBuffer.toString());
    }
}

Обратите внимание, как appendReplacement() вызывается внутри цикла while(matcher.find()), а appendTail() вызывается сразу после цикла.

Выход из этого примера:

Joe Blocks
Joe Blocks about this, and Joe Blocks
Joe Blocks about this, and Joe Blocks writes about that, and Joe Blocks
Joe Blocks about this, and Joe Blocks writes about that, and Joe Blocks
    writes about everything.

Разрыв строки в последней строке вставлен мной, чтобы сделать текст более читабельным. В реальном выводе не было бы разрыва строки.

Как видите, StringBuffer состоит из символов и замен из входного текста, по одному совпадению за раз.

Оцените статью