Normal ifade eşleşmeleri dizisi oluşturma


160

Java, ben bir diziye tüm regex maçları döndürmeye çalışıyorum ama sadece desen bir şey eşleşip eşleşmediğini (boolean) kontrol edebilirsiniz gibi görünüyor.

Belirli bir dizede normal ifade ile eşleşen tüm dizelerden oluşan bir dizi oluşturmak için normal ifade eşleşmesini nasıl kullanabilirim?


2
İyi soru. Aradığınız bilgiler Regex ve Matcher'daki Java belgelerinin bir parçası olmalıdır. Ne yazık ki öyle değil.
Cheeso

3
Gerçek bir utanç. Bu işlevsellik, neredeyse her dilde (düzenli ifade desteğine sahip) kutudan çıkmış gibi görünüyor.
Ray Toal

Yanıtlar:


278

( Java> = 9 varsayabilirseniz, 4castle'ın cevabı aşağıdakilerden daha iyidir)

Bir eşleştirici oluşturmanız ve bunu eşleşmeli olarak eşleşmeleri bulmak için kullanmanız gerekir.

 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());
 }

Bundan sonra allMatches, eşleşmeleri içerir ve allMatches.toArray(new String[0])gerçekten bir diziye ihtiyacınız varsa bir dizi almak için kullanabilirsiniz .


Geçerli grup durumunun anlık görüntüsünü döndürdüğünden MatchResult, eşleşmeler arasında döngü oluşturmak için yardımcı işlevler yazmak için de kullanabilirsiniz Matcher.toMatchResult().

Örneğin, yapmanıza izin vermek için tembel bir yineleyici yazabilirsiniz

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

böyle bir şey yaparak:

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(); }
      };
    }
  };
}

Bununla,

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

verim

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
Boyutu önceden bilmiyorsanız ve arabellek yeniden boyutlandırmasından kaçınmak isteyebileceğiniz için burada bir ArrayList kullanmanızı önermem. Bunun yerine, LinkedList'i tercih ederim - sadece bir öneri olsa da ve cevabınızı daha az geçerli kılmaz.
Liv

13
@Liv, ikisini de kıyaslamak için zaman ayırın ArrayListve LinkedListsonuçlar şaşırtıcı olabilir.
Anthony Accioly

Söylediklerinizi duyuyorum ve her iki durumda da yürütme hızı ve bellek ayak izinin farkındayım; ArrayList ile ilgili sorun, varsayılan oluşturucunun 10 kapasite oluşturmasıdır - eğer ekleyeceğiniz çağrılarla bu boyutu geçerseniz ( ) bellek ayırma ve dizi kopyalama işlemlerini üstlenmeniz gerekir ve bu birkaç kez gerçekleşebilir. Eğer sadece birkaç maç bekliyorsanız, yaklaşımınız daha verimli olacaktır; ancak dizi "yeniden boyutlandırma" bir kereden fazla gerçekleştiğini bulursanız, bir LinkedList öneririm, hatta daha düşük bir gecikme uygulaması ile ilgileniyorsanız.
Liv

12
@Liv, Modeliniz oldukça tahmin edilebilir bir boyuta sahip eşleşmeler üretme eğilimindeyse ve desenin seyrek veya yoğun olarak eşleşmesine bağlı olarak ( allMatchesvs uzunluklarının toplamına bağlı olarak yourStringHere.length()), muhtemelen iyi bir boyut önceden hesaplayabilirsiniz allMatches. Deneyimlerime göre, LinkedListbellek maliyeti ve yineleme verimliliği açısından genellikle buna değmez, bu yüzden LinkedListvarsayılan duruşum değil. Ancak bir etkin noktayı optimize ederken, bir iyileşme elde edip etmediğinizi görmek için liste uygulamalarını değiştirmeye kesinlikle değer.
Mike Samuel

1
Java 9, artık kullanabilirsiniz Matcher#resultsbir olsun Streamsize bir dizi oluşturmak için kullanabileceğiniz (bkz cevabımı ).
4castle

56

Java 9'da, artık Matcher#results()birStream<MatchResult> bir eşleşmeler liste / dizisini almak için kullanabilir.

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
onların hiçbir sonuç () yöntemi, lütfen önce bunu çalıştırın
Bravo

14
@Bravo Java 9 mu kullanıyorsunuz? Var. Belgelere bağlandım.
4castle

: ((java 8 için herhangi bir alternatif var mı
logbasex

25

Java normal ifadeyi çok karmaşık hale getirir ve perl stilini takip etmez. Bunu tek bir Java kodu satırında nasıl başarabileceğinizi görmek için MentaRegex'e göz atın :

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

6
Çok havalı. Çift eğik çizgi hala çirkin gözüküyor ama sanırım ondan bir sapma yok.
JohnPristine

mentaregex-0.9.5.jar, 6Kb günümü kurtaran Obrigado Sérgio!
CONvid19

2
DİKKAT! En iyi çözüm. Kullanın!
Vlad Holubiev

14
MentaRegex sitesi kapalı mı? Mentaregex.soliveirajr.com ziyaret ettiğimde sadece "merhaba" diyor
user64141

1
@ user64141 öyle görünüyor
Amit Gold

11

İşte basit bir örnek:

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

(daha fazla yakalama grubunuz varsa, grup yöntemine bağımsız değişken olarak dizinlerine göre bunlara başvurabilirsiniz. Bir diziye ihtiyacınız varsa kullanın list.toArray())


pattern.matches (input) çalışmıyor. Normal ifadenizi (tekrar!) -> WTF Java? pattern.matches (String regex, String girişi); Şunu mu demek istedin pattern.matcher (input)?
El Mac

@ElMac Pattern.matches()statik bir yöntemdir, bunu bir Patternörnek üzerinde çağırmamalısınız . Pattern.matches(regex, input)sadece bir kısayoldur Pattern.compile(regex).matcher(input).matches().
dimo414

5

Gönderen Resmi 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;
        }

Sonucunu diziniz / Liste / öğenizde kullanın findve ekleyin 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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.