Создать массив совпадений регулярных выражений


160

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

Как я могу использовать соответствие регулярному выражению для формирования массива всех строк, соответствующих выражению регулярного выражения в данной строке?


2
Хороший вопрос. Информация, которую вы ищете, должна быть частью документации Java по Regex и Matcher. К сожалению, это не так.
Cheeso

3
Настоящий позор. Эта функциональность, кажется, существует из коробки практически на любом другом языке (который поддерживает регулярные выражения).
Рэй Тоал

Ответы:


278

( Ответ 4castle лучше, чем приведенный ниже, если вы предполагаете, что Java> = 9)

Вам нужно создать сопоставление и использовать его для итеративного поиска совпадений.

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

 ...

 List<String> allMatches = new ArrayList<String>();
 Matcher m = Pattern.compile("your regular expression here")
     .matcher(yourStringHere);
 while (m.find()) {
   allMatches.add(m.group());
 }

После этого allMatchesсодержит совпадения, и вы можете использовать их allMatches.toArray(new String[0])для получения массива, если он вам действительно нужен.


Вы также можете использовать MatchResultдля написания вспомогательных функций для зацикливания совпадений, поскольку Matcher.toMatchResult()возвращает снимок текущего состояния группы.

Например, вы можете написать ленивый итератор, чтобы позволить вам сделать

for (MatchResult match : allMatches(pattern, input)) {
  // Use match, and maybe break without doing the work to find all possible matches.
}

делая что-то вроде этого:

public static Iterable<MatchResult> allMatches(
      final Pattern p, final CharSequence input) {
  return new Iterable<MatchResult>() {
    public Iterator<MatchResult> iterator() {
      return new Iterator<MatchResult>() {
        // Use a matcher internally.
        final Matcher matcher = p.matcher(input);
        // Keep a match around that supports any interleaving of hasNext/next calls.
        MatchResult pending;

        public boolean hasNext() {
          // Lazily fill pending, and avoid calling find() multiple times if the
          // clients call hasNext() repeatedly before sampling via next().
          if (pending == null && matcher.find()) {
            pending = matcher.toMatchResult();
          }
          return pending != null;
        }

        public MatchResult next() {
          // Fill pending if necessary (as when clients call next() without
          // checking hasNext()), throw if not possible.
          if (!hasNext()) { throw new NoSuchElementException(); }
          // Consume pending so next call to hasNext() does a find().
          MatchResult next = pending;
          pending = null;
          return next;
        }

        /** Required to satisfy the interface, but unsupported. */
        public void remove() { throw new UnsupportedOperationException(); }
      };
    }
  };
}

С этим,

for (MatchResult match : allMatches(Pattern.compile("[abc]"), "abracadabra")) {
  System.out.println(match.group() + " at " + match.start());
}

доходность

a at 0
b at 1
a at 3
c at 4
a at 5
a at 7
b at 8
a at 10

4
Я бы не советовал использовать ArrayList здесь, так как вы не знаете заранее размер и можете избежать изменения размера буфера. Вместо этого я бы предпочел LinkedList - хотя это всего лишь предложение и не делает ваш ответ менее действительным вообще.
Лив

13
@Liv, найдите время, чтобы сравнить оба, ArrayListи LinkedListрезультаты могут быть удивительными.
Энтони Аксиоли

Я слышу, что вы говорите, и я осознаю скорость выполнения и объем памяти в обоих случаях: проблема с ArrayList заключается в том, что конструктор по умолчанию создает емкость 10 - если вы превысите этот размер с помощью вызовов add ( ) вам придется иметь дело с распределением памяти и копированием массива - и это может случиться несколько раз. Конечно, если вы ожидаете всего несколько совпадений, то ваш подход более эффективен; однако если вы обнаружите, что «изменение размера» массива происходит несколько раз, я бы предложил LinkedList, особенно если вы работаете с приложением с низкой задержкой.
Лив

12
@Liv, если ваш шаблон имеет тенденцию производить совпадения с довольно предсказуемым размером, и в зависимости от того, соответствует ли шаблон редко или плотно (на основе суммы длин allMatchesvs yourStringHere.length()), вы, вероятно, можете заранее рассчитать хороший размер для allMatches. По моему опыту, затраты LinkedListпамяти и эффективности итераций обычно не стоят того, поэтому я LinkedListне по умолчанию. Но при оптимизации хот-спота, безусловно, стоит поменять реализации списка, чтобы увидеть, есть ли у вас улучшения.
Майк Сэмюэл

1
В Java 9, теперь вы можете использовать , Matcher#resultsчтобы получить Streamкоторый вы можете использовать для создания массива (см моего ответа ).
4каст

56

В Java 9 теперь вы можете использовать Matcher#results()для получения, Stream<MatchResult>который вы можете использовать, чтобы получить список / массив совпадений.

import java.util.regex.Pattern;
import java.util.regex.MatchResult;
String[] matches = Pattern.compile("your regex here")
                          .matcher("string to search from here")
                          .results()
                          .map(MatchResult::group)
                          .toArray(String[]::new);
                    // or .collect(Collectors.toList())

1
их нет результатов () метод, пожалуйста, запустите это сначала
Браво

14
@Bravo Вы используете Java 9? Это существует. Я связался с документацией.
4каста

: ((есть ли альтернатива для Java 8
logbasex

25

Java делает регулярные выражения слишком сложными и не соответствует стилю perl. Взгляните на MentaRegex, чтобы увидеть, как вы можете сделать это в одной строке кода Java:

String[] matches = match("aa11bb22", "/(\\d+)/g" ); // => ["11", "22"]

6
Это классно. Двойная косая черта все еще выглядит некрасиво, но я думаю, что от этого нет смысла.
JohnPristine

mentaregex-0.9.5.jar, 6Kb, который спас мой день, Обригадо Сержио!
CONvid19

2
ВНИМАНИЕ! Лучшее решение Используй это!
Влад Голубев

14
Сайт MentaRegex не работает? Когда я захожу на mentaregex.soliveirajr.com, он говорит только «привет»
user64141

1
@ user64141 выглядит так
Амит Голд

11

Вот простой пример:

Pattern pattern = Pattern.compile(regexPattern);
List<String> list = new ArrayList<String>();
Matcher m = pattern.matcher(input);
while (m.find()) {
    list.add(m.group());
}

(если у вас есть больше групп захвата, вы можете ссылаться на них по их индексу в качестве аргумента метода группы. Если вам нужен массив, используйте list.toArray())


pattern.matches (вход) не работает. Вы должны передать свой шаблон регулярных выражений (снова!) -> WTF Java ?! pattern.matches (строковое регулярное выражение, строковый ввод); Вы имеете в виду pattern.matcher (вход)?
Эль Мак

@ElMac Pattern.matches()- статический метод, вы не должны вызывать его в Patternэкземпляре. Pattern.matches(regex, input)это просто сокращение для Pattern.compile(regex).matcher(input).matches().
dimo414

5

С официальных Regex Java Trails :

        Pattern pattern = 
        Pattern.compile(console.readLine("%nEnter your regex: "));

        Matcher matcher = 
        pattern.matcher(console.readLine("Enter input string to search: "));

        boolean found = false;
        while (matcher.find()) {
            console.format("I found the text \"%s\" starting at " +
               "index %d and ending at index %d.%n",
                matcher.group(), matcher.start(), matcher.end());
            found = true;
        }

Использование findи вставить полученный groupв массиве / List / что угодно.


0
        Set<String> keyList = new HashSet();
        Pattern regex = Pattern.compile("#\\{(.*?)\\}");
        Matcher matcher = regex.matcher("Content goes here");
        while(matcher.find()) {
            keyList.add(matcher.group(1)); 
        }
        return keyList;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.