Buat berbagai pertandingan regex

160

Di Jawa, saya mencoba mengembalikan semua kecocokan regex ke array tetapi tampaknya Anda hanya dapat memeriksa apakah polanya cocok dengan sesuatu atau tidak (boolean).

Bagaimana saya bisa menggunakan pencocokan regex untuk membentuk array dari semua string yang cocok dengan ekspresi regex dalam string yang diberikan?

Jake Sankey
sumber
2
Pertanyaan bagus. Informasi yang Anda cari harus menjadi bagian dari dokumen Java tentang Regex dan Matcher. Sayangnya, tidak.
Cheeso
3
Sayang sekali. Fungsi ini tampaknya ada di luar kotak di hampir setiap bahasa lain (yang memiliki dukungan ekspresi reguler).
Ray Toal

Jawaban:

278

( Jawaban 4castle lebih baik daripada yang di bawah ini jika Anda dapat menganggap Java> = 9)

Anda perlu membuat korek api dan menggunakannya untuk menemukan korek api.

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

Setelah ini, allMatchesberisi kecocokan, dan Anda dapat menggunakan allMatches.toArray(new String[0])untuk mendapatkan array jika Anda benar-benar membutuhkannya.


Anda juga dapat menggunakan MatchResultuntuk menulis fungsi pembantu untuk mengulangi pertandingan karena Matcher.toMatchResult()mengembalikan snapshot dari status grup saat ini.

Misalnya, Anda dapat menulis iterator malas untuk membiarkan Anda melakukannya

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

dengan melakukan sesuatu seperti ini:

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

Dengan ini,

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

hasil panen

a at 0
b at 1
a at 3
c at 4
a at 5
a at 7
b at 8
a at 10
Mike Samuel
sumber
4
Saya tidak akan menyarankan menggunakan ArrayList di sini karena Anda tidak tahu di muka ukurannya dan mungkin ingin menghindari ukuran buffer. Sebaliknya, saya lebih suka LinkedList - meskipun itu hanya saran dan tidak membuat jawaban Anda kurang valid sama sekali.
Liv
13
@Liv, luangkan waktu untuk membandingkan keduanya ArrayListdan LinkedList, hasilnya mungkin mengejutkan.
Anthony Accioly
Saya mendengar apa yang Anda katakan dan saya menyadari kecepatan eksekusi dan jejak memori dalam kedua kasus; masalah dengan ArrayList adalah bahwa konstruktor default menciptakan kapasitas 10 - jika Anda melewati ukuran itu dengan panggilan untuk menambahkan ( ) Anda harus menanggung dengan alokasi memori dan salinan array - dan itu mungkin terjadi beberapa kali. Memang, jika Anda mengharapkan hanya beberapa kecocokan maka pendekatan Anda adalah yang lebih efisien; Namun jika Anda menemukan bahwa array "mengubah ukuran" terjadi lebih dari sekali saya akan menyarankan LinkedList, bahkan lebih jika Anda berurusan dengan aplikasi latensi rendah.
Liv
12
@Liv, Jika pola Anda cenderung menghasilkan kecocokan dengan ukuran yang cukup dapat diprediksi, dan tergantung pada apakah pola tersebut cocok dengan sedikit atau padat (berdasarkan jumlah panjang allMatchesvs yourStringHere.length()), Anda mungkin dapat melakukan prakiraan ukuran yang baik untuk allMatches. Dalam pengalaman saya, biaya LinkedListmemori dan efisiensi iterasi-bijaksana biasanya tidak sepadan jadi LinkedListbukan postur default saya. Tetapi ketika mengoptimalkan hot-spot, itu pasti layak implementasi swapping daftar untuk melihat apakah Anda mendapatkan perbaikan.
Mike Samuel
1
Di Java 9, sekarang Anda dapat menggunakan Matcher#resultsuntuk mendapatkan Streamyang dapat Anda gunakan untuk menghasilkan array (lihat jawaban saya ).
4castle
56

Di Java 9, sekarang Anda dapat menggunakan Matcher#results()untuk mendapatkan Stream<MatchResult>yang dapat Anda gunakan untuk mendapatkan daftar / array yang cocok.

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())
4castle
sumber
1
mereka tidak ada hasil () metode, silakan jalankan ini pertama
Bravo
14
@Bravo Apakah Anda menggunakan Java 9? Itu memang ada. Saya tertaut ke dokumentasi.
4castle
: ((apakah ada alternatif untuk java 8
logbasex
25

Java membuat regex terlalu rumit dan tidak mengikuti gaya perl. Lihatlah MentaRegex untuk melihat bagaimana Anda bisa mencapainya dalam satu baris kode Java:

String[] matches = match("aa11bb22", "/(\\d+)/g" ); // => ["11", "22"]
TraderJoeChicago
sumber
6
Itu keren. Slash ganda masih terlihat jelek tapi kurasa tidak ada scape dari itu.
JohnPristine
mentaregex-0.9.5.jar, 6Kb yang menyelamatkan hari saya, Obrigado Sérgio!
CONvid19
2
PERHATIAN! Solusi terbaik. Gunakan!
Vlad Holubiev
14
Apakah situs MentaRegex turun? Ketika saya mengunjungi mentaregex.soliveirajr.com itu hanya mengatakan "hai"
user64141
1
@ user64141 sepertinya
Amit Gold
11

Berikut ini contoh sederhana:

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

(jika Anda memiliki lebih banyak grup penangkap, Anda dapat merujuk mereka dengan indeks mereka sebagai argumen dari metode grup. Jika Anda memerlukan sebuah array, maka gunakan list.toArray())

Bozho
sumber
pattern.matches (input) tidak berfungsi. Anda harus melewati pola regex Anda (lagi!) -> WTF Java ?! pattern.matches (String regex, input String); Apakah maksud Anda pattern.matcher (input)?
El Mac
@ ElMac Pattern.matches()adalah metode statis, Anda tidak boleh menyebutnya sebagai Patterncontoh. Pattern.matches(regex, input)hanyalah sebuah singkatan untuk Pattern.compile(regex).matcher(input).matches().
dimo414
5

Dari Jalur Resmi Regex Java :

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

Gunakan finddan masukkan hasilnya grouppada array / Daftar / apa pun Anda.

Anthony Accioly
sumber
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;
Nikhil Kumar K
sumber