Створіть масив збігів регулярних виразів


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, і результати можуть бути дивними.
Anthony Accioly

Я чую, що ви говорите, і я знаю про швидкість виконання та слід пам’яті в обох випадках; проблема з ArrayList полягає в тому, що конструктор за замовчуванням створює ємність 10 - якщо ви переходите цей розмір з викликами для додавання ( ) вам доведеться мати місце з розподілом пам'яті та копією масиву - і це може трапитися кілька разів. Зрозуміло, якщо ви очікуєте лише декількох матчів, тоді ваш підхід є більш ефективним; якщо ви виявите, що масив "зміни розміру" відбувається не раз, я б запропонував LinkedList, тим більше, якщо ви маєте справу з додатком із низькою затримкою.
Лив

12
@Liv, Якщо ваш малюнок має тенденцію створювати збіги з досить передбачуваним розміром, і залежно від того, чи відповідає малюнок рідко чи щільно (виходячи з суми довжин allMatchesvs yourStringHere.length()), ви, ймовірно, можете попередньо обчислити хороший розмір allMatches. На мій досвід, вартість LinkedListпам'яті та ефективність ітерації, як правило, не вартують цього, тому LinkedListце не моя позиція за замовчуванням. Але під час оптимізації гарячої точки, безумовно, варто поміняти реалізацію списку, щоб побачити, чи отримаєте ви покращення.
Майк Самуель

1
Тепер у Java 9 ви можете скористатися Matcher#resultsдля отримання атрибута, Streamякий ви можете використовувати для створення масиву (див. Мою відповідь ).
4castle

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? Воно існує. Я зв'язав документацію.
4castle

: ((чи є альтернатива для java 8
logbasex

25

Java робить регулярний вираз занадто складним, і він не відповідає стилю perl. Погляньте на MentaRegex, щоб побачити, як ви можете досягти цього в одному рядку коду Java:

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

6
Круто. Подвійна коса риса все ще виглядає потворно, але я гадаю, що від цього немає жодного сказу.
JohnPristine

mentaregex-0.9.5.jar, 6Kb, що врятувало мені день, Obrigado Sérgio!
CONvid19

2
УВАГА! Найкраще рішення. Використай це!
Влад Голубієв

14
Чи не працює сайт MentaRegex? Коли я відвідую mentaregex.soliveirajr.com, він говорить лише "привіт"
user64141

1
@ user64141 виглядає так, як це
Amit Gold

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 (Строковий регекс, String input); Ви маєте на увазі pattern.matcher (вхід)?
El Mac

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

5

Від офіційних маршрутів Java Regex :

        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у свій масив / Список / що завгодно.


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.