Menjodohkan dengan ekspresi reguler

Jika Anda telah memprogram dalam Perl atau bahasa lain dengan kemampuan ekspresi reguler bawaan, Anda mungkin tahu betapa mudahnya ekspresi reguler membuat pemrosesan teks dan pencocokan pola. Jika Anda tidak terbiasa dengan istilah tersebut, ekspresi reguler hanyalah serangkaian karakter yang menentukan pola yang digunakan untuk mencari string yang cocok.

Banyak bahasa, termasuk Perl, PHP, Python, JavaScript, dan JScript, sekarang mendukung ekspresi reguler untuk pemrosesan teks, dan beberapa editor teks menggunakan ekspresi reguler untuk fungsionalitas pencarian-dan-ganti yang canggih. Bagaimana dengan Java? Pada saat penulisan ini, Permintaan Spesifikasi Java yang menyertakan pustaka ekspresi reguler untuk pemrosesan teks telah disetujui; Anda dapat melihatnya di versi JDK yang akan datang.

Tetapi bagaimana jika Anda membutuhkan pustaka ekspresi reguler sekarang? Untungnya, Anda dapat mengunduh perpustakaan ORO Jakarta open source dari Apache.org. Pada artikel ini, saya akan memberi Anda penjelasan singkat tentang ekspresi reguler, dan kemudian saya akan menunjukkan kepada Anda bagaimana menggunakan ekspresi reguler dengan API Jakarta-ORO open source.

Ekspresi reguler 101

Mari kita mulai dengan sederhana. Misalkan Anda ingin mencari string dengan kata "cat" di dalamnya; ekspresi reguler Anda akan menjadi "kucing". Jika penelusuran Anda tidak membedakan huruf besar / kecil, kata "katalog", "Catherine", atau "canggih" juga akan cocok:

Ekspresi reguler: kucing

Cocok: kucing, katalog, Catherine, canggih

Notasi periode

Bayangkan Anda bermain Scrabble dan membutuhkan kata tiga huruf yang dimulai dengan huruf "t" dan diakhiri dengan huruf "n". Bayangkan juga bahwa Anda memiliki kamus bahasa Inggris dan akan mencari seluruh isinya untuk mencari kecocokan menggunakan ekspresi reguler. Untuk membentuk ekspresi reguler seperti itu, Anda akan menggunakan notasi karakter pengganti - karakter titik (.). Ekspresi reguler kemudian akan menjadi "tn" dan akan cocok dengan "tan", "Ten", "tin", dan "ton"; itu juga akan cocok dengan "t # n", "tpn", dan bahkan "t n", serta banyak kata tidak masuk akal lainnya. Ini karena karakter titik cocok dengan semuanya, termasuk spasi, karakter tab, dan bahkan jeda baris:

Ekspresi reguler: tn

Cocok: tan, Sepuluh, timah, ton, tn, t # n, tpn, dll.

Notasi braket

Untuk mengatasi masalah pencocokan sembarangan periode, Anda dapat menentukan karakter yang Anda anggap bermakna dengan ekspresi tanda kurung ("[]"), sehingga hanya karakter tersebut yang akan cocok dengan ekspresi reguler. Jadi, "t [aeio] n" hanya akan cocok dengan "tan", "Ten", "tin", dan "ton". "Toon" tidak akan cocok karena Anda hanya dapat mencocokkan satu karakter dalam notasi tanda kurung:

Ekspresi reguler: t [aeio] n

Cocok: tan, Sepuluh, timah, ton

Operator OR

Jika Anda ingin mencocokkan "toon" selain semua kata yang cocok di bagian sebelumnya, Anda dapat menggunakan "|" notasi, yang pada dasarnya adalah operator OR. Untuk mencocokkan "toon", gunakan ekspresi reguler "t (a | e | i | o | oo) n". Anda tidak dapat menggunakan notasi kurung siku di sini karena hanya akan cocok dengan satu karakter. Sebagai gantinya, gunakan tanda kurung - "()". Anda juga dapat menggunakan tanda kurung untuk pengelompokan (lebih lanjut tentang itu nanti):

Ekspresi reguler: t (a | e | i | o | oo) n

Cocok: tan, Sepuluh, timah, ton, toon

Notasi pembilang

Tabel 1 menunjukkan notasi pembilang yang digunakan untuk menentukan berapa kali notasi yang diberikan di sebelah kiri langsung dari notasi pembilang harus berulang:

Tabel 1. Notasi pengukur
Notasi Berkali-kali
* 0 kali atau lebih
+ 1 kali atau lebih
? 0 atau 1 kali
{n} Tepat n kali
{n, m} n hingga m beberapa kali

Misalkan Anda ingin mencari nomor jaminan sosial di file teks. Format nomor jaminan sosial AS adalah 999-99-9999. Ekspresi reguler yang akan Anda gunakan untuk mencocokkan ini ditunjukkan pada Gambar 1. Dalam ekspresi reguler, notasi tanda hubung ("-") memiliki arti khusus; ini menunjukkan rentang yang akan cocok dengan nomor apa pun dari 0 hingga 9. Akibatnya, Anda harus melepaskan karakter "-" dengan garis miring ("\") saat mencocokkan tanda hubung literal di nomor jaminan sosial.

Jika, dalam penelusuran Anda, Anda ingin membuat tanda hubung opsional - jika, katakanlah, Anda menganggap format 999-99-9999 dan 999999999 dapat diterima - Anda dapat menggunakan "?" notasi pembilang. Gambar 2 menunjukkan ekspresi reguler:

Mari kita lihat contoh lainnya. Satu format untuk nomor plat mobil AS terdiri dari empat karakter numerik diikuti oleh dua huruf. Ekspresi reguler pertama kali terdiri dari bagian numerik, "[0-9] {4}", diikuti oleh bagian tekstual, "[AZ] {2}". Gambar 3 menunjukkan ekspresi reguler lengkap:

Notasi NOT

Notasi "^" juga disebut notasi NOT. Jika digunakan dalam tanda kurung, "^" menunjukkan karakter yang tidak ingin Anda cocokkan. Misalnya, ekspresi pada Gambar 4 cocok dengan semua kata

kecuali

yang dimulai dengan huruf X.

Tanda kurung dan spasi

Misalnya Anda mencoba mengekstrak bulan lahir dari tanggal lahir seseorang. Tanggal lahir tipikal dalam format berikut: 26 Juni 1951. Ekspresi reguler untuk mencocokkan string akan seperti yang ada di Gambar 5:

The new "\s" notation is the space notation and matches all blank spaces, including tabs. If the string matches perfectly, how do you extract the month field? You simply put parentheses around the month field, creating a group, and later retrieve the value using the ORO API (discussed in a following section). The appropriate regular expression is in Figure 6:

Other miscellaneous notations

To make life easier, some shorthand notations for commonly used regular expressions have been created, as shown in Table 2:

Table 2. Commonly used notations
Notation Equivalent Notation
\d [0-9]
\D [^0-9]
\w [A-Z0-9]
\W [^A-Z0-9]
\s [ \t\n\r\f]
\S [^ \t\n\r\f]

To illustrate, we can use "\d" for all instances of "[0-9]" we used before, as was the case with our social security number expressions. The revised regular expression is in Figure 7:

Jakarta-ORO library

Many open source regular expression libraries are available for Java programmers, and many support the Perl 5-compatible regular expression syntax. I use the Jakarta-ORO regular expression library because it is one of the most comprehensive APIs available and is fully compatible with Perl 5 regular expressions. It is also one of the most optimized APIs around.

The Jakarta-ORO library was formerly known as OROMatcher and has been kindly donated to the Jakarta Project by Daniel Savarese. You can download the package from a link in the Resources section below.

The Jakarta-ORO objects

I'll start by briefly describing the objects you need to create and access in order to use this library, and then I will show how you use the Jakarta-ORO API.

The PatternCompiler object

First, create an instance of the Perl5Compiler class and assign it to the PatternCompiler interface object. Perl5Compiler is an implementation of the PatternCompiler interface and lets you compile a regular expression string into a Pattern object used for matching:

 PatternCompiler compiler=new Perl5Compiler(); 

The Pattern object

To compile a regular expression into a

Pattern

object, call the

compile()

method of the compiler object, passing in the regular expression. For example, you can compile the regular expression

"t[aeio]n"

like so:

 Pattern pattern=null; try { pattern=compiler.compile("t[aeio]n"); } catch (MalformedPatternException e) { e.printStackTrace(); } 

By default, the compiler creates a case-sensitive pattern, so that the above setup only matches "tin", "tan", "ten", and "ton", but not "Tin" or "taN". To create a case-insensitive pattern, you would call a compiler with an additional mask:

 pattern=compiler.compile("t[aeio]n",Perl5Compiler.CASE_INSENSITIVE_MASK); 

Once you've created the Pattern object, you can use it for pattern matching with the PatternMatcher class.

The PatternMatcher object

The PatternMatcher object tests for a match based on the Pattern object and a string. You instantiate a Perl5Matcher class and assign it to the PatternMatcher interface. The Perl5Matcher class is an implementation of the PatternMatcher interface and matches patterns based on the Perl 5 regular expression syntax:

 PatternMatcher matcher=new Perl5Matcher(); 

You can obtain a match using the PatternMatcher object in one of several ways, with the string to be matched against the regular expression passed in as the first parameter:

  • boolean matches(String input, Pattern pattern): Used if the input string and the regular expression should match exactly; in other words, the regular expression should totally describe the string input
  • boolean matchesPrefix(String input, Pattern pattern): Used if the regular expression should match the beginning of the input string
  • boolean contains(String input, Pattern pattern): Used if the regular expression should match part of the input string (i.e., should be a substring)

You could also pass in a PatternMatcherInput object instead of a String object to the above three method calls; if you did so, you could continue matching from the point at which the last match was found in the string. This is useful when you have many substrings that are likely to be matched by a given regular expression. The method signatures with the PatternMatcherInput object instead of String are as follows:

  • boolean matches(PatternMatcherInput input, Pattern pattern)
  • boolean matchesPrefix(PatternMatcherInput input, Pattern pattern)
  • boolean contains(PatternMatcherInput input, Pattern pattern)

Scenarios for using the API

Now let's discuss some example uses of the Jakarta-ORO library.

Log file processing

Your job: analyze a Web server log file and determine how long each user spends on the Website. An entry from a typical BEA WebLogic log file looks like this:

172.26.155.241 - - [26/Feb/2001:10:56:03 -0500] "GET /IsAlive.htm HTTP/1.0" 200 15 

After analyzing this entry, you'll realize that you need to extract two things from the log file: the IP address and a page's access time. You can use the grouping notation (parentheses) to extract the IP address field and the timestamp field from the log entry.

Let's first discuss the IP address. It consists of 4 bytes, each with values between 0 and 255; each byte is separated from the others by a period. Thus, in each individual byte in the IP address, you have at least one and at most three digits. You can see the regular expression for this field in Figure 8:

You need to escape the period character because you literally want it to be there; you do not want it read in terms of its special meaning in regular expression syntax, which I explained earlier.

The log entry's timestamp part is surrounded by square brackets. You can extract whatever is within these brackets by first searching for the opening square bracket character ("[") and extracting whatever is not within the closing square bracket character ("]"), continuing until you reach the closing square bracket. Figure 9 shows the regular expression for this:

Now you combine these two regular expressions into a single expression with grouping notation (parentheses) for extraction of your IP address and timestamp. Notice that "\s-\s-\s" is added in the middle so that matching occurs, although you won't extract that. You can see the complete regular expression in Figure 10.

Now that you've formulated this regular expression, you can begin writing Java code using the regular expression library.

Using the Jakarta-ORO library

To begin using the Jakarta-ORO library, first create the regular expression string and the sample string to parse:

 String logEntry="172.26.155.241 - - [26/Feb/2001:10:56:03 -0500] \"GET /IsAlive.htm HTTP/1.0\" 200 15 "; String regexp="([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})\\s-\\s-\\s\\[([^\\]]+)\\]"; 

The regular expression used here is nearly identical to the one found in Figure 10, with only one difference: in Java, you need to escape every forward slash ("\"). Figure 10 is not in Java, so we need to escape the forward-slash character so as not to cause a compilation error. Unfortunately, this process is prone to error and you must do it carefully. You can type in the regular expression first without escaping the forward slashes, and then visually scan the string from left to right and replace every occurrence of the "\" character with "\\". To double check, print out the resulting string to the console.

After initializing the strings, instantiate the PatternCompiler object and create a Pattern object by using the PatternCompiler to compile the regular expression:

Compiler PatternCompiler = Perl5Compiler baru (); Pola pola = compiler.compile (regexp);

Sekarang, buat PatternMatcherobjek dan panggil contain()metode di PatternMatcherantarmuka untuk melihat apakah Anda memiliki kecocokan: