Ekspresi reguler di Java, Bagian 1: Pencocokan pola dan kelas Pola

Karakter Java dan kelas string bermacam-macam menawarkan dukungan tingkat rendah untuk pencocokan pola, tetapi dukungan itu biasanya mengarah ke kode yang kompleks. Untuk pengkodean yang lebih sederhana dan lebih efisien, Java menawarkan Regex API. Tutorial dua bagian ini membantu Anda memulai ekspresi reguler dan API Regex. Pertama kita akan membongkar tiga kelas kuat yang berada di dalam java.util.regexpaket, kemudian kita akan menjelajahi Patternkelas dan konstruksi pencocokan pola yang canggih.

unduh Dapatkan kodenya Unduh kode sumber untuk aplikasi contoh dalam tutorial ini. Dibuat oleh Jeff Friesen untuk JavaWorld.

Apa itu ekspresi reguler?

Sebuah ekspresi reguler , juga dikenal sebagai regex atau regexp , adalah string yang pola (template) menggambarkan satu set string. Pola tersebut menentukan string mana yang termasuk dalam himpunan. Pola terdiri dari karakter literal dan karakter meta , yaitu karakter yang memiliki makna khusus, bukan makna literal.

Pencocokan pola adalah proses pencarian teks untuk mengidentifikasi kecocokan , atau string yang cocok dengan pola regex. Java mendukung pencocokan pola melalui Regex API-nya. API terdiri dari tiga classes-- Pattern, Matcherdan PatternSyntaxException--all terletak di java.util.regexpaket:

  • Patternobjek, juga dikenal sebagai pola , adalah regex yang dikompilasi.
  • Matcherobjek, atau pencocokan , adalah mesin yang menafsirkan pola untuk menemukan kecocokan dalam urutan karakter (objek yang kelasnya mengimplementasikan java.lang.CharSequenceantarmuka dan berfungsi sebagai sumber teks).
  • PatternSyntaxException objek mendeskripsikan pola regex ilegal.

Java juga menyediakan dukungan untuk pencocokan pola melalui berbagai metode di java.lang.Stringkelasnya. Misalnya, boolean matches(String regex)mengembalikan nilai true hanya jika string pemanggilan sama persis regexdengan regex.

Metode kenyamanan

Di balik layar, matches()dan Stringmetode kenyamanan berorientasi regex lainnya diimplementasikan dalam hal Regex API.

RegexDemo

Saya telah menciptakan RegexDemoaplikasi untuk menunjukkan ekspresi Jawa reguler dan berbagai metode yang terletak di Pattern, Matcher, dan PatternSyntaxExceptionkelas. Berikut kode sumber untuk demo:

Kode 1. Mendemonstrasikan regex

import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; public class RegexDemo { public static void main(String[] args) { if (args.length != 2) { System.err.println("usage: java RegexDemo regex input"); return; } // Convert new-line (\n) character sequences to new-line characters. args[1] = args[1].replaceAll("\\\\n", "\n"); try { System.out.println("regex = " + args[0]); System.out.println("input = " + args[1]); Pattern p = Pattern.compile(args[0]); Matcher m = p.matcher(args[1]); while (m.find()) System.out.println("Found [" + m.group() + "] starting at " + m.start() + " and ending at " + (m.end() - 1)); } catch (PatternSyntaxException pse) { System.err.println("Bad regex: " + pse.getMessage()); System.err.println("Description: " + pse.getDescription()); System.err.println("Index: " + pse.getIndex()); System.err.println("Incorrect pattern: " + pse.getPattern()); } } }

Hal pertama RegexDemo's main()metode yang dilakukan adalah untuk memvalidasi baris perintah. Ini membutuhkan dua argumen: argumen pertama adalah regex, dan argumen kedua adalah teks input yang akan dicocokkan dengan regex.

Anda mungkin ingin menentukan karakter new-line ( \n) sebagai bagian dari teks input. Satu-satunya cara untuk melakukannya adalah dengan menentukan \karakter yang diikuti oleh nkarakter. main()mengonversi urutan karakter ini ke nilai Unicode 10.

Sebagian besar RegexDemokode terletak di try- catchkonstruksi. The tryblok pertama output tertentu regex dan input teks dan kemudian menciptakan Patternobjek yang menyimpan regex dikompilasi. (Regex dikompilasi untuk meningkatkan kinerja selama pencocokan pola.) Pencocokan diekstraksi dari Patternobjek dan digunakan untuk berulang kali mencari kecocokan hingga tidak ada yang tersisa. The catchblok memanggil berbagai PatternSyntaxExceptionmetode untuk mengekstrak informasi yang berguna tentang pengecualian. Informasi ini kemudian dikeluarkan.

Anda tidak perlu mengetahui lebih banyak tentang cara kerja kode sumber pada saat ini; ini akan menjadi jelas saat Anda menjelajahi API di Bagian 2. Namun, Anda memang perlu mengkompilasi Daftar 1. Ambil kode dari Daftar 1, lalu ketikkan yang berikut ini ke dalam baris perintah Anda untuk dikompilasi RegexDemo:

javac RegexDemo.java

Pola dan konstruksinya

Pattern, kelas pertama dari tiga kelas yang terdiri dari Regex API, adalah representasi terkompilasi dari ekspresi reguler. PatternDokumentasi SDK menjelaskan berbagai konstruksi regex, tetapi kecuali Anda sudah menjadi pengguna regex yang rajin, Anda mungkin bingung dengan bagian-bagian dokumentasi. Apa itu bilangan dan apa perbedaan antara bilangan serakah , enggan , dan posesif ? Apa kelas karakter , pencocokan batas , referensi kembali , dan ekspresi bendera yang disematkan ? Saya akan menjawab pertanyaan-pertanyaan ini dan lebih banyak lagi di bagian selanjutnya.

String literal

Konstruksi regex paling sederhana adalah string literal. Beberapa bagian dari teks masukan harus cocok dengan pola konstruksi ini agar pola yang cocok berhasil. Perhatikan contoh berikut:

java RegexDemo apple applet

Contoh ini mencoba menemukan apakah ada kecocokan untuk applepola dalam appletteks masukan. Output berikut menunjukkan kecocokan:

regex = apple input = applet Found [apple] starting at 0 and ending at 4

Keluarannya menunjukkan ekspresi reguler dan teks masukan, lalu menunjukkan kecocokan yang berhasil di appledalamnya applet. Selain itu, ini menyajikan indeks awal dan akhir dari pertandingan itu: 0dan 4, masing-masing. Indeks awal mengidentifikasi lokasi teks pertama di mana pola yang cocok terjadi; indeks akhir mengidentifikasi lokasi teks terakhir untuk pertandingan tersebut.

Sekarang misalkan kita menentukan baris perintah berikut:

java RegexDemo apple crabapple

Kali ini, kami mendapatkan pertandingan berikut dengan indeks awal dan akhir yang berbeda:

regex = apple input = crabapple Found [apple] starting at 4 and ending at 8

Skenario kebalikannya, di mana appletregex dan applemerupakan teks masukan, tidak menunjukkan kecocokan. Seluruh regex harus cocok, dan dalam hal ini teks masukan tidak berisi tsetelah apple.

Metakarakter

Konstruksi regex yang lebih kuat menggabungkan karakter literal dengan karakter meta. Misalnya, dalam a.b, periode metacharacter ( .) mewakili karakter apa pun yang muncul di antara adan b. Perhatikan contoh berikut:

java RegexDemo .ox "The quick brown fox jumps over the lazy ox."

Contoh ini ditetapkan .oxsebagai regex dan The quick brown fox jumps over the lazy ox.sebagai teks masukan. RegexDemomencari teks untuk kecocokan yang dimulai dengan karakter apa pun dan diakhiri dengan ox. Ini menghasilkan keluaran sebagai berikut:

regex = .ox input = The quick brown fox jumps over the lazy ox. Found [fox] starting at 16 and ending at 18 Found [ ox] starting at 39 and ending at 41

The output reveals two matches: fox and ox (with the leading space character). The . metacharacter matches the f in the first match and the space character in the second match.

What happens when we replace .ox with the period metacharacter? That is, what output results from specifying the following command line:

java RegexDemo . "The quick brown fox jumps over the lazy ox."

Because the period metacharacter matches any character, RegexDemo outputs a match for each character (including the terminating period character) in the input text:

regex = . input = The quick brown fox jumps over the lazy ox. Found [T] starting at 0 and ending at 0 Found [h] starting at 1 and ending at 1 Found [e] starting at 2 and ending at 2 Found [ ] starting at 3 and ending at 3 Found [q] starting at 4 and ending at 4 Found [u] starting at 5 and ending at 5 Found [i] starting at 6 and ending at 6 Found [c] starting at 7 and ending at 7 Found [k] starting at 8 and ending at 8 Found [ ] starting at 9 and ending at 9 Found [b] starting at 10 and ending at 10 Found [r] starting at 11 and ending at 11 Found [o] starting at 12 and ending at 12 Found [w] starting at 13 and ending at 13 Found [n] starting at 14 and ending at 14 Found [ ] starting at 15 and ending at 15 Found [f] starting at 16 and ending at 16 Found [o] starting at 17 and ending at 17 Found [x] starting at 18 and ending at 18 Found [ ] starting at 19 and ending at 19 Found [j] starting at 20 and ending at 20 Found [u] starting at 21 and ending at 21 Found [m] starting at 22 and ending at 22 Found [p] starting at 23 and ending at 23 Found [s] starting at 24 and ending at 24 Found [ ] starting at 25 and ending at 25 Found [o] starting at 26 and ending at 26 Found [v] starting at 27 and ending at 27 Found [e] starting at 28 and ending at 28 Found [r] starting at 29 and ending at 29 Found [ ] starting at 30 and ending at 30 Found [t] starting at 31 and ending at 31 Found [h] starting at 32 and ending at 32 Found [e] starting at 33 and ending at 33 Found [ ] starting at 34 and ending at 34 Found [l] starting at 35 and ending at 35 Found [a] starting at 36 and ending at 36 Found [z] starting at 37 and ending at 37 Found [y] starting at 38 and ending at 38 Found [ ] starting at 39 and ending at 39 Found [o] starting at 40 and ending at 40 Found [x] starting at 41 and ending at 41 Found [.] starting at 42 and ending at 42

Quoting metacharacters

To specify . or any metacharacter as a literal character in a regex construct, quote the metacharacter in one of the following ways:

  • Precede the metacharacter with a backslash character.
  • Place the metacharacter between \Q and \E (e.g., \Q.\E).

Remember to double each backslash character (as in \\. or \\Q.\\E) that appears in a string literal such as String regex = "\\.";. Don't double the backslash character when it appears as part of a command-line argument.

Character classes

We sometimes need to limit characters that will produce matches to a specific character set. For example, we might search text for vowels a, e, i, o, and u, where any occurrence of a vowel indicates a match. A character class identifies a set of characters between square-bracket metacharacters ([ ]), helping us accomplish this task. Pattern supports simple, negation, range, union, intersection, and subtraction character classes. We'll look at all of these below.

Simple character class

The simple character class consists of characters placed side by side and matches only those characters. For example, [abc] matches characters a, b, and c.

Consider the following example:

java RegexDemo [csw] cave

This example matches only c with its counterpart in cave, as shown in the following output:

regex = [csw] input = cave Found [c] starting at 0 and ending at 0

Negation character class

The negation character class begins with the ^ metacharacter and matches only those characters not located in that class. For example, [^abc] matches all characters except a, b, and c.

Consider this example:

java RegexDemo "[^csw]" cave

Note that the double quotes are necessary on my Windows platform, whose shell treats the ^ character as an escape character.

This example matches a, v, and e with their counterparts in cave, as shown here:

regex = [^csw] input = cave Found [a] starting at 1 and ending at 1 Found [v] starting at 2 and ending at 2 Found [e] starting at 3 and ending at 3

Range character class

The range character class consists of two characters separated by a hyphen metacharacter (-). All characters beginning with the character on the left of the hyphen and ending with the character on the right of the hyphen belong to the range. For example, [a-z] matches all lowercase alphabetic characters. It's equivalent to specifying [abcdefghijklmnopqrstuvwxyz].

Consider the following example:

java RegexDemo [a-c] clown

This example matches only c with its counterpart in clown, as shown:

regex = [a-c] input = clown Found [c] starting at 0 and ending at 0

Merging multiple ranges

You can merge multiple ranges into the same range character class by placing them side by side. For example, [a-zA-Z] matches all lowercase and uppercase alphabetic characters.

Union character class

The union character class consists of multiple nested character classes and matches all characters that belong to the resulting union. For example, [a-d[m-p]] matches characters a through d and m through p.

Consider the following example:

java RegexDemo [ab[c-e]] abcdef

This example matches a, b, c, d, and e with their counterparts in abcdef:

regex = [ab[c-e]] input = abcdef Found [a] starting at 0 and ending at 0 Found [b] starting at 1 and ending at 1 Found [c] starting at 2 and ending at 2 Found [d] starting at 3 and ending at 3 Found [e] starting at 4 and ending at 4

Intersection character class

The intersection character class consists of characters common to all nested classes and matches only common characters. For example, [a-z&&[d-f]] matches characters d, e, and f.

Consider the following example:

java RegexDemo "[aeiouy&&[y]]" party

Perhatikan bahwa tanda kutip ganda diperlukan di platform Windows saya, yang shellnya memperlakukan &karakter sebagai pemisah perintah.

Contoh ini hanya cocok ydengan mitranya di party:

regex = [aeiouy&&[y]] input = party Found [y] starting at 4 and ending at 4