CodeGym /Blog Java /rawak /Ungkapan biasa dalam Java
John Squirrels
Tahap
San Francisco

Ungkapan biasa dalam Java

Diterbitkan dalam kumpulan
Ungkapan biasa ialah topik yang pengaturcara, malah yang berpengalaman, sering menangguhkan untuk kemudian. Tetapi lambat laun, kebanyakan pembangun Java perlu memproses maklumat teks. Selalunya, ini bermakna mencari dan mengedit teks. Tanpa ungkapan biasa, kod pemprosesan teks yang berkesan dan padat tidak dapat difikirkan. Jadi berhenti berlengah-lengah, mari kita atasi ungkapan biasa sekarang. Ia tidak begitu sukar. Ungkapan biasa dalam Java - 1

Apakah ungkapan biasa (regex)?

Malah, ungkapan biasa ialah corak untuk mencari rentetan dalam teks. Di Jawa, perwakilan asal corak ini sentiasa rentetan, iaitu objek kelas String. Walau bagaimanapun, ia bukan sebarang rentetan yang boleh disusun menjadi ungkapan biasa — hanya rentetan yang mematuhi peraturan untuk mencipta ungkapan biasa. Sintaks ditakrifkan dalam spesifikasi bahasa. Ungkapan biasa ditulis menggunakan huruf dan nombor, serta metacharacters, iaitu aksara yang mempunyai makna istimewa dalam sintaks ungkapan biasa. Sebagai contoh:

String regex = "java"; // The pattern is "java";
String regex = "\\d{3}"; // The pattern is three digits;

Mencipta ungkapan biasa dalam Java

Mencipta ungkapan biasa dalam Java melibatkan dua langkah mudah:
  1. tuliskannya sebagai rentetan yang mematuhi sintaks ungkapan biasa;
  2. menyusun rentetan menjadi ungkapan biasa;
Dalam mana-mana program Java, kami mula bekerja dengan ungkapan biasa dengan mencipta Patternobjek. Untuk melakukan ini, kita perlu memanggil salah satu daripada dua kaedah statik kelas: compile. Kaedah pertama mengambil satu hujah — rentetan literal yang mengandungi ungkapan biasa, manakala kaedah kedua mengambil hujah tambahan yang menentukan tetapan padanan corak:

public static Pattern compile (String literal)
public static Pattern compile (String literal, int flags)
Senarai nilai potensi parameter flagsditakrifkan dalam Patternkelas dan tersedia kepada kami sebagai pembolehubah kelas statik. Sebagai contoh:

Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE); // Pattern-matching will be case insensitive.
Pada asasnya, Patternkelas adalah pembina untuk ungkapan biasa. Di bawah tudung, compilekaedah memanggil Patternpembina peribadi kelas untuk mencipta perwakilan yang disusun. Mekanisme penciptaan objek ini dilaksanakan dengan cara ini untuk mencipta objek tidak berubah. Apabila ungkapan biasa dibuat, sintaksnya diperiksa. Jika rentetan mengandungi ralat, maka a PatternSyntaxExceptiondijana.

Sintaks ungkapan biasa

Sintaks ungkapan biasa bergantung pada <([{\^-=$!|]})?*+.>aksara, yang boleh digabungkan dengan huruf. Bergantung kepada peranan mereka, mereka boleh dibahagikan kepada beberapa kumpulan:
1. Metacharacter untuk memadankan sempadan baris atau teks
Metacharacter Penerangan
^ permulaan baris
$ hujung baris
\b sempadan perkataan
\B sempadan bukan perkataan
\A permulaan input
\G tamat perlawanan sebelumnya
\Z akhir input
\z akhir input
2. Metacharacter untuk memadankan kelas aksara yang dipratentukan
Metacharacter Penerangan
\d digit
\D bukan digit
\s watak ruang putih
\S aksara bukan ruang putih
\w aksara abjad angka atau garis bawah
\W sebarang aksara kecuali huruf, nombor dan garis bawah
. sebarang watak
3. Metacharacter untuk memadankan aksara kawalan
Metacharacter Penerangan
\t watak tab
\n watak baris baharu
\r pemulangan pengangkutan
\f watak suapan baris
\u0085 watak baris seterusnya
\u2028 pemisah garis
\u2029 pemisah perenggan
4. Metacharacter untuk kelas watak
Metacharacter Penerangan
[abc] mana-mana aksara yang disenaraikan (a, b, atau c)
[^abc] sebarang aksara selain daripada yang disenaraikan (bukan a, b, atau c)
[a-zA-Z] julat digabungkan (aksara Latin dari a hingga z, tidak peka huruf besar-besaran)
[iklan[mp]] penyatuan aksara (dari a ke d dan dari m ke p)
[az&&[def]] persilangan aksara (d, e, f)
[az&&[^bc]] penolakan aksara (a, dz)
5. Metacharacters untuk menunjukkan bilangan aksara (pengkuantiti). Pengkuantiti sentiasa didahului oleh watak atau kumpulan watak.
Metacharacter Penerangan
? satu atau tiada
* sifar atau lebih kali
+ satu kali atau lebih
{n} n kali
{n,} n atau lebih kali
{n,m} sekurang-kurangnya n kali dan tidak lebih daripada m kali

Pengkuantiti yang tamak

Satu perkara yang perlu anda ketahui tentang pengkuantiti ialah ia datang dalam tiga jenis berbeza: tamak, posesif dan enggan. Anda membuat pengkuantiti posesif dengan menambahkan +aksara " " selepas pengkuantiti. Anda membuatnya keberatan dengan menambah " ?". Sebagai contoh:

"A.+a" // greedy
"A.++a" // possessive
"A.+?a" // reluctant
Mari cuba gunakan corak ini untuk memahami cara pelbagai jenis pengkuantiti berfungsi. Secara lalai, pengkuantiti adalah tamak. Ini bermakna mereka mencari perlawanan terpanjang dalam rentetan. Jika kita menjalankan kod berikut:

public static void main(String[] args) {
    String text = "Fred Anna Alexander";
    Pattern pattern = Pattern.compile("A.+a");
    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        System.out.println(text.substring(matcher.start(), matcher.end()));
    }
}
kami mendapat output ini:

Anna Alexa
Untuk ungkapan biasa " A.+a", padanan corak dilakukan seperti berikut:
  1. Aksara pertama dalam corak yang ditentukan ialah huruf Latin A. Matchermembandingkannya dengan setiap aksara teks, bermula dari sifar indeks. Watak Fberada pada indeks sifar dalam teks kami, jadi Matcherberulang melalui aksara sehingga ia sepadan dengan corak. Dalam contoh kami, watak ini terdapat pada indeks 5.

    Ungkapan biasa dalam Java - 2
  2. Setelah padanan dengan watak pertama corak ditemui, Matchercari padanan dengan watak kedua. Dalam kes kami, ia ialah .watak " ", yang bermaksud mana-mana watak.

    Ungkapan biasa dalam Java - 3

    Watak itu nberada di kedudukan keenam. Ia sudah tentu layak sebagai perlawanan untuk "sebarang watak".

  3. Matcherterus menyemak watak corak seterusnya. Dalam corak kami, ia termasuk dalam pengkuantiti yang digunakan pada aksara sebelumnya: " .+". Oleh kerana bilangan ulangan "mana-mana aksara" dalam corak kami adalah satu atau lebih kali, Matcherberulang kali mengambil aksara seterusnya daripada rentetan dan menyemaknya terhadap corak selagi ia sepadan dengan "mana-mana watak". Dalam contoh kami — sehingga akhir rentetan (dari indeks 7 hingga indeks 18).

    Ungkapan biasa dalam Java - 4

    Pada asasnya, Matchermelahap rentetan hingga akhir — inilah yang dimaksudkan dengan "tamak".

  4. Selepas Matcher mencapai penghujung teks dan menyelesaikan semakan untuk A.+bahagian " " corak, ia mula menyemak corak yang lain: a. Tiada lagi teks ke hadapan, jadi semakan diteruskan dengan "undur", bermula dari aksara terakhir:

    Ungkapan biasa dalam Java - 5
  5. Matcher"mengingat" bilangan ulangan dalam .+bahagian " " corak. Pada ketika ini, ia mengurangkan bilangan ulangan sebanyak satu dan menyemak corak yang lebih besar terhadap teks sehingga padanan ditemui:

    Ungkapan biasa dalam Java - 6

Pengkuantiti posesif

Pengkuantiti posesif adalah sama seperti yang tamak. Perbezaannya ialah apabila teks telah ditangkap ke penghujung rentetan, tiada padanan corak semasa "undur". Dalam erti kata lain, tiga peringkat pertama adalah sama seperti pengkuantiti tamak. Selepas menangkap keseluruhan rentetan, pemadan menambahkan selebihnya corak pada apa yang sedang dipertimbangkan dan membandingkannya dengan rentetan yang ditangkap. Dalam contoh kami, menggunakan ungkapan biasa " A.++a", kaedah utama tidak menemui padanan. Ungkapan biasa dalam Java - 7

Pengkuantiti enggan

  1. Untuk pengkuantiti ini, seperti varieti tamak, kod mencari padanan berdasarkan aksara pertama corak:

    Ungkapan biasa dalam Java - 8
  2. Kemudian ia mencari padanan dengan watak seterusnya corak (sebarang watak):

    Ungkapan biasa dalam Java - 9
  3. Tidak seperti padanan corak tamak, padanan terpendek dicari dalam padanan corak enggan. Ini bermakna bahawa selepas mencari padanan dengan aksara kedua corak (titik, yang sepadan dengan aksara pada kedudukan 6 dalam teks, Matchermenyemak sama ada teks sepadan dengan corak yang lain — aksara " a"

    Ungkapan biasa dalam Java - 10
  4. Teks tidak sepadan dengan corak (iaitu ia mengandungi aksara " n" pada indeks 7), jadi Matchermenambah satu "mana-mana aksara", kerana pengkuantiti menunjukkan satu atau lebih. Kemudian ia sekali lagi membandingkan corak dengan teks dalam kedudukan 5 hingga 8:

    Ungkapan biasa dalam Java - 11
  5. Dalam kes kami, padanan ditemui, tetapi kami belum sampai ke penghujung teks lagi. Oleh itu, padanan corak dimulakan semula dari kedudukan 9, iaitu aksara pertama corak dicari menggunakan algoritma yang serupa dan ini berulang sehingga akhir teks.

    Ungkapan biasa dalam Java - 12
Sehubungan itu, mainkaedah memperoleh hasil berikut apabila menggunakan corak " A.+?a": Anna Alexa Seperti yang anda boleh lihat daripada contoh kami, jenis pengkuantiti yang berbeza menghasilkan keputusan yang berbeza untuk corak yang sama. Oleh itu, ingatlah perkara ini dan pilih varieti yang betul berdasarkan perkara yang anda cari.

Melarikan diri aksara dalam ungkapan biasa

Oleh kerana ungkapan biasa dalam Java, atau lebih tepatnya, perwakilan asalnya, ialah literal rentetan, kita perlu mengambil kira peraturan Java berkenaan literal rentetan. Khususnya, aksara backslash " \" dalam literal rentetan dalam kod sumber Java ditafsirkan sebagai watak kawalan yang memberitahu pengkompil bahawa aksara seterusnya adalah istimewa dan mesti ditafsirkan dengan cara yang istimewa. Sebagai contoh:

String s = "The root directory is \nWindows"; // Move "Windows" to a new line
String s = "The root directory is \u00A7Windows"; // Insert a paragraph symbol before "Windows"
Ini bermakna literal rentetan yang menerangkan ungkapan biasa dan menggunakan \aksara " " (iaitu untuk menunjukkan aksara meta) mesti mengulang garis miring ke belakang untuk memastikan pengkompil kod bait Java tidak menyalahtafsir rentetan. Sebagai contoh:

String regex = "\\s"; // Pattern for matching a whitespace character
String regex = "\"Windows\"";  // Pattern for matching "Windows"
Garis miring ke belakang berganda juga mesti digunakan untuk melepaskan aksara khas yang kita mahu gunakan sebagai aksara "biasa". Sebagai contoh:

String regex = "How\\?";  // Pattern for matching "How?"

Kaedah kelas Corak

Kelas Patternmempunyai kaedah lain untuk bekerja dengan ungkapan biasa:
  • String pattern()‒ mengembalikan perwakilan rentetan asal ungkapan biasa yang digunakan untuk mencipta Patternobjek:

    
    Pattern pattern = Pattern.compile("abc");
    System.out.println(pattern.pattern()); // "abc"
    
  • static boolean matches(String regex, CharSequence input)– membolehkan anda menyemak ungkapan biasa yang diluluskan sebagai regex terhadap teks yang diluluskan sebagai input. Pulangan:

    benar – jika teks sepadan dengan corak;
    palsu – jika tidak;

    Sebagai contoh:

    
    System.out.println(Pattern.matches("A.+a","Anna")); // true
    System.out.println(Pattern.matches("A.+a","Fred Anna Alexander")); // false
    
  • int flags()‒ mengembalikan nilai flagsset parameter corak apabila corak dicipta atau 0 jika parameter tidak ditetapkan. Sebagai contoh:

    
    Pattern pattern = Pattern.compile("abc");
    System.out.println(pattern.flags()); // 0
    Pattern pattern = Pattern.compile("abc",Pattern.CASE_INSENSITIVE);
    System.out.println(pattern.flags()); // 2
    
  • String[] split(CharSequence text, int limit)– membahagikan teks yang diluluskan kepada Stringtatasusunan. Parameter limitmenunjukkan bilangan maksimum padanan yang dicari dalam teks:

    • jika limit > 0limit-1sepadan;
    • jika limit < 0‒ semua sepadan dalam teks
    • jika limit = 0‒ semua padanan dalam teks, rentetan kosong pada penghujung tatasusunan akan dibuang;

    Sebagai contoh:

    
    public static void main(String[] args) {
        String text = "Fred Anna Alexa";
        Pattern pattern = Pattern.compile("\\s");
        String[] strings = pattern.split(text,2);
        for (String s : strings) {
            System.out.println(s);
        }
        System.out.println("---------");
        String[] strings1 = pattern.split(text);
        for (String s : strings1) {
            System.out.println(s);
        }
    }
    

    Output konsol:

    
    Fred
    Anna Alexa
    ---------
    Fred
    Anna
    Alexa
    

    Di bawah ini kita akan mempertimbangkan satu lagi kaedah kelas yang digunakan untuk mencipta Matcherobjek.

Kaedah kelas Matcher

Kejadian kelas Matcherdibuat untuk melakukan padanan corak. Matcherialah "enjin carian" untuk ungkapan biasa. Untuk melakukan carian, kita perlu memberikannya dua perkara: corak dan indeks permulaan. Untuk mencipta Matcherobjek, Patternkelas menyediakan kaedah berikut: рublic Matcher matcher(CharSequence input) Kaedah mengambil urutan aksara, yang akan dicari. Ini ialah contoh kelas yang melaksanakan CharSequenceantara muka. Anda boleh lulus bukan sahaja a String, tetapi juga a StringBuffer, StringBuilder, Segment, atau CharBuffer. Corak adalah Patternobjek di mana matcherkaedah dipanggil. Contoh membuat padanan:

Pattern p = Pattern.compile("a*b"); // Create a compiled representation of the regular expression
Matcher m = p.matcher("aaaaab"); // Create a "search engine" to search the text "aaaaab" for the pattern "a*b"
Kini kami boleh menggunakan "enjin carian" kami untuk mencari padanan, mendapatkan kedudukan padanan dalam teks dan menggantikan teks menggunakan kaedah kelas. Kaedah boolean find()mencari padanan seterusnya dalam teks. Kita boleh menggunakan kaedah ini dan pernyataan gelung untuk menganalisis keseluruhan teks sebagai sebahagian daripada model peristiwa. Dalam erti kata lain, kita boleh melakukan operasi yang diperlukan apabila sesuatu peristiwa berlaku, iaitu apabila kita menemui padanan dalam teks. Sebagai contoh, kita boleh menggunakan kelas int start()dan int end()kaedah ini untuk menentukan kedudukan padanan dalam teks. Dan kita boleh menggunakan kaedah String replaceFirst(String replacement)dan String replaceAll(String replacement)untuk menggantikan padanan dengan nilai parameter penggantian. Sebagai contoh:

public static void main(String[] args) {
    String text = "Fred Anna Alexa";
    Pattern pattern = Pattern.compile("A.+?a");

    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        int start=matcher.start();
        int end=matcher.end();
        System.out.println("Match found: " + text.substring(start, end) + " from index "+ start + " through " + (end-1));
    }
    System.out.println(matcher.replaceFirst("Ira"));
    System.out.println(matcher.replaceAll("Mary"));
    System.out.println(text);
}
Pengeluaran:

Match found: Anna from index 5 through 8
Match found: Alexa from index 10 through 14
Fred Ira Alexa
Fred Mary Mary
Fred Anna Alexa
Contoh menjelaskan bahawa kaedah replaceFirstdan replaceAllmencipta Stringobjek baharu — rentetan di mana corak padanan dalam teks asal digantikan dengan teks yang dihantar kepada kaedah sebagai hujah. Selain itu, replaceFirstkaedah menggantikan hanya padanan pertama, tetapi replaceAllkaedah menggantikan semua padanan dalam teks. Teks asal kekal tidak berubah. Operasi regex yang paling kerap Patterndan Matcherkelas dibina terus ke dalam Stringkelas. Ini adalah kaedah seperti split, matches, replaceFirstdan replaceAll. Tetapi di bawah tudung, kaedah ini menggunakan Patterndan Matcherkelas. Jadi jika anda ingin menggantikan teks atau membandingkan rentetan dalam atur cara tanpa menulis sebarang kod tambahan, gunakan kaedahStringkelas. Jika anda memerlukan ciri yang lebih maju, ingat Patterndan Matcherkelas.

Kesimpulan

Dalam program Java, ungkapan biasa ditakrifkan oleh rentetan yang mematuhi peraturan padanan corak tertentu. Apabila melaksanakan kod, mesin Java menyusun rentetan ini ke dalam Patternobjek dan menggunakan Matcherobjek untuk mencari padanan dalam teks. Seperti yang saya katakan pada mulanya, orang sering menangguhkan ungkapan biasa untuk kemudian, menganggapnya sebagai topik yang sukar. Tetapi jika anda memahami sintaks asas, aksara meta, dan pelarian aksara, dan mengkaji contoh ungkapan biasa, maka anda akan mendapati ia lebih mudah daripada yang dipaparkan pada pandangan pertama.

Bacaan lanjut:

Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION