Ang mga regular na expression ay isang paksa na madalas ipagpaliban ng mga programmer, kahit na ang mga may karanasan, para sa ibang pagkakataon. Ngunit maaga o huli, karamihan sa mga developer ng Java ay kailangang magproseso ng impormasyong tekstuwal. Kadalasan, nangangahulugan ito ng paghahanap at pag-edit ng teksto. Kung walang mga regular na expression, ang epektibo at compact na text-processing code ay hindi maiisip. Kaya itigil ang pagpapaliban, talakayin natin ang mga regular na expression ngayon. Hindi naman ganoon kahirap. Mga regular na expression sa Java - 1

Ano ang isang regular na expression (regex)?

Sa katunayan, ang isang regular na expression ay isang pattern para sa paghahanap ng isang string sa teksto. Sa Java, ang orihinal na representasyon ng pattern na ito ay palaging isang string, ibig sabihin, isang bagay ng Stringklase. Gayunpaman, hindi ito anumang string na maaaring i-compile sa isang regular na expression — mga string lamang na sumusunod sa mga panuntunan para sa paglikha ng mga regular na expression. Ang syntax ay tinukoy sa detalye ng wika. Ang mga regular na expression ay isinusulat gamit ang mga titik at numero, pati na rin ang mga metacharacter, na mga character na may espesyal na kahulugan sa regular na expression syntax. Halimbawa:

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

Paglikha ng mga regular na expression sa Java

Ang paglikha ng isang regular na expression sa Java ay nagsasangkot ng dalawang simpleng hakbang:
  1. isulat ito bilang isang string na sumusunod sa regular na expression syntax;
  2. ipunin ang string sa isang regular na expression;
Sa anumang programa ng Java, nagsisimula kaming magtrabaho kasama ang mga regular na expression sa pamamagitan ng paglikha ng isang Patternbagay. Upang gawin ito, kailangan nating tawagan ang isa sa dalawang static na pamamaraan ng klase: compile. Ang unang paraan ay tumatagal ng isang argumento — isang string na literal na naglalaman ng regular na expression, habang ang pangalawa ay kumukuha ng karagdagang argumento na tumutukoy sa mga setting ng pagtutugma ng pattern:

public static Pattern compile (String literal)
public static Pattern compile (String literal, int flags)
Ang listahan ng mga potensyal na halaga ng flagsparameter ay tinukoy sa Patternklase at magagamit sa amin bilang mga static na variable ng klase. Halimbawa:

Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE); // Pattern-matching will be case insensitive.
Karaniwan, ang Patternklase ay isang tagabuo para sa mga regular na expression. Sa ilalim ng hood, ang compilepamamaraan ay tumatawag sa Patternpribadong constructor ng klase upang lumikha ng isang pinagsama-samang representasyon. Ang mekanismo ng paglikha ng bagay na ito ay ipinatupad sa ganitong paraan upang lumikha ng mga hindi nababagong bagay. Kapag ang isang regular na expression ay nilikha, ang syntax nito ay nasuri. Kung ang string ay naglalaman ng mga error, a PatternSyntaxExceptionay nabuo.

Syntax ng regular na expression

Ang syntax ng regular na expression ay umaasa sa mga <([{\^-=$!|]})?*+.>character, na maaaring isama sa mga titik. Depende sa kanilang tungkulin, maaari silang nahahati sa ilang mga grupo:
1. Metacharacter para sa pagtutugma ng mga hangganan ng mga linya o teksto
Metacharacter Paglalarawan
^ simula ng isang linya
$ dulo ng isang linya
\b hangganan ng salita
\B di-salitang hangganan
\A simula ng input
\G pagtatapos ng nakaraang laban
\Z dulo ng input
\z dulo ng input
2. Metacharacter para sa pagtutugma ng mga paunang natukoy na klase ng character
Metacharacter Paglalarawan
\d digit
\D di-digit
\s character ng whitespace
\S character na hindi whitespace
\w alphanumeric na character o underscore
\W anumang karakter maliban sa mga titik, numero, at salungguhit
. anumang karakter
3. Metacharacter para sa pagtutugma ng mga control character
Metacharacter Paglalarawan
\t karakter ng tab
\n bagong linyang karakter
\r pagbabalik ng karwahe
\f karakter ng linefeed
\u0085 karakter sa susunod na linya
\u2028 separator ng linya
\u2029 separator ng talata
4. Metacharacter para sa mga klase ng character
Metacharacter Paglalarawan
[abc] alinman sa mga nakalistang character (a, b, o c)
[^abc] anumang karakter maliban sa mga nakalista (hindi a, b, o c)
[a-zA-Z] pinagsamang mga hanay (mga Latin na character mula a hanggang z, case insensitive)
[ad[mp]] unyon ng mga character (mula a hanggang d at mula m hanggang p)
[az&&[def]] intersection ng mga character (d, e, f)
[az&&[^bc]] pagbabawas ng mga character (a, dz)
5. Metacharacter para sa pagtukoy ng bilang ng mga character (quantifiers). Ang isang quantifier ay palaging nauuna sa isang karakter o pangkat ng character.
Metacharacter Paglalarawan
? isa o wala
* zero o higit pang beses
+ isa o higit pang beses
{n} n beses
{n,} n o higit pang beses
{n,m} hindi bababa sa n beses at hindi hihigit sa m beses

Mga sakim na quantifier

Ang isang bagay na dapat mong malaman tungkol sa mga quantifier ay ang mga ito ay nagmumula sa tatlong magkakaibang uri: sakim, nagmamay-ari, at nag-aatubili. Ginagawa mong possessive ang quantifier sa pamamagitan ng pagdaragdag ng +character na " " pagkatapos ng quantifier. Nag-aatubili ka sa pamamagitan ng pagdaragdag ng " ?". Halimbawa:

"A.+a" // greedy
"A.++a" // possessive
"A.+?a" // reluctant
Subukan nating gamitin ang pattern na ito upang maunawaan kung paano gumagana ang iba't ibang uri ng mga quantifier. Bilang default, ang mga quantifier ay matakaw. Nangangahulugan ito na hinahanap nila ang pinakamahabang tugma sa string. Kung patakbuhin namin ang sumusunod na code:

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()));
    }
}
nakukuha namin ang output na ito:

Anna Alexa
Para sa regular na expression na " A.+a", ang pattern-matching ay isinasagawa bilang mga sumusunod:
  1. Ang unang character sa tinukoy na pattern ay ang Latin na titik A. Matcherinihahambing ito sa bawat karakter ng teksto, simula sa index zero. Ang character Fay nasa index zero sa aming teksto, kaya Matcherumuulit sa mga character hanggang sa tumugma ito sa pattern. Sa aming halimbawa, ang karakter na ito ay matatagpuan sa index 5.

    Mga regular na expression sa Java - 2
  2. Kapag natagpuan ang isang tugma sa unang character ng pattern, Matchermaghahanap ng tugma sa pangalawang character nito. Sa aming kaso, ito ay ang .karakter na " ", na kumakatawan sa anumang karakter.

    Mga regular na expression sa Java - 3

    Ang karakter nay nasa ikaanim na posisyon. Tiyak na kwalipikado ito bilang isang tugma para sa "anumang karakter".

  3. Matchernagpapatuloy upang suriin ang susunod na karakter ng pattern. Sa aming pattern, kasama ito sa quantifier na nalalapat sa naunang character: " .+". Dahil ang bilang ng mga pag-uulit ng "anumang character" sa aming pattern ay isa o higit pang beses, Matcherpaulit-ulit na kinukuha ang susunod na character mula sa string at sinusuri ito laban sa pattern hangga't tumutugma ito sa "anumang character." Sa aming halimbawa — hanggang sa dulo ng string (mula index 7 hanggang index 18).

    Mga regular na expression sa Java - 4

    Karaniwang, Matchernilalamon ang string hanggang sa dulo — ito mismo ang ibig sabihin ng "matakaw".

  4. Pagkatapos maabot ng Matcher ang dulo ng text at tapusin ang pagsuri para sa " A.+" bahagi ng pattern, magsisimula itong suriin ang natitirang pattern: a. Wala nang text na pasulong, kaya ang pagsusuri ay nagpapatuloy sa pamamagitan ng "pag-back off", simula sa huling karakter:

    Mga regular na expression sa Java - 5
  5. Matcher"naaalala" ang bilang ng mga pag-uulit sa " .+" bahagi ng pattern. Sa puntong ito, binabawasan nito ang bilang ng mga pag-uulit ng isa at sinusuri ang mas malaking pattern laban sa teksto hanggang sa matagpuan ang isang tugma:

    Mga regular na expression sa Java - 6

Possessive quantifiers

Ang mga possessive quantifier ay katulad ng mga sakim. Ang kaibahan ay kapag ang teksto ay nakuha hanggang sa dulo ng string, walang pattern-matching habang "umatras". Sa madaling salita, ang unang tatlong yugto ay kapareho ng para sa mga matakaw na quantifier. Pagkatapos makuha ang buong string, idaragdag ng matcher ang natitirang pattern sa kung ano ang isinasaalang-alang nito at ihahambing ito sa nakuhang string. Sa aming halimbawa, gamit ang regular na expression na " A.++a", ang pangunahing paraan ay walang mahanap na tugma. Mga regular na expression sa Java - 7

Nag-aatubili na mga quantifier

  1. Para sa mga quantifier na ito, tulad ng sakim na iba't, ang code ay naghahanap ng isang tugma batay sa unang character ng pattern:

    Mga regular na expression sa Java - 8
  2. Pagkatapos ay naghahanap ito ng tugma sa susunod na karakter ng pattern (anumang karakter):

    Mga regular na expression sa Java - 9
  3. Hindi tulad ng matakaw na pattern-matching, ang pinakamaikling tugma ay hinahanap sa nag-aatubili na pattern-matching. Nangangahulugan ito na pagkatapos makahanap ng tugma sa pangalawang karakter ng pattern (isang tuldok, na tumutugma sa karakter sa posisyon 6 sa teksto, Matchersinusuri kung ang teksto ay tumutugma sa natitirang pattern — ang karakter na " a"

    Mga regular na expression sa Java - 10
  4. Ang teksto ay hindi tumutugma sa pattern (ibig sabihin, naglalaman ito ng character na " n" sa index 7), kaya Matchernagdaragdag ng higit pang isang "anumang character", dahil ang quantifier ay nagpapahiwatig ng isa o higit pa. Pagkatapos ay muli nitong ikinukumpara ang pattern sa teksto sa mga posisyon 5 hanggang 8:

    Mga regular na expression sa Java - 11
  5. Sa aming kaso, may nakitang tugma, ngunit hindi pa namin naabot ang dulo ng text. Samakatuwid, ang pattern-matching ay magsisimula muli mula sa posisyon 9, ibig sabihin, ang unang character ng pattern ay hinahanap gamit ang isang katulad na algorithm at ito ay umuulit hanggang sa katapusan ng teksto.

    Mga regular na expression sa Java - 12
Alinsunod dito, mainnakukuha ng pamamaraan ang sumusunod na resulta kapag ginagamit ang pattern na " A.+?a": Anna Alexa Gaya ng nakikita mo mula sa aming halimbawa, ang iba't ibang uri ng quantifier ay gumagawa ng iba't ibang resulta para sa parehong pattern. Kaya isaisip ito at piliin ang tamang uri batay sa hinahanap mo.

Pagtakas sa mga character sa mga regular na expression

Dahil ang isang regular na expression sa Java, o sa halip, ang orihinal na representasyon nito, ay literal na string, kailangan nating isaalang-alang ang mga panuntunan ng Java tungkol sa mga literal na string. Sa partikular, ang backslash na character na " \" sa mga string literal sa Java source code ay binibigyang kahulugan bilang isang control character na nagsasabi sa compiler na ang susunod na character ay espesyal at dapat bigyang-kahulugan sa isang espesyal na paraan. Halimbawa:

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"
Nangangahulugan ito na ang mga literal na string na naglalarawan ng mga regular na expression at gumagamit ng \mga character na " " (ibig sabihin, upang ipahiwatig ang mga metacharacter) ay dapat ulitin ang mga backslashes upang matiyak na ang Java bytecode compiler ay hindi mali ang kahulugan ng string. Halimbawa:

String regex = "\\s"; // Pattern for matching a whitespace character
String regex = "\"Windows\"";  // Pattern for matching "Windows"
Dapat ding gumamit ng dobleng backslashes para makatakas sa mga espesyal na character na gusto naming gamitin bilang "normal" na mga character. Halimbawa:

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

Mga pamamaraan ng klase ng Pattern

Ang Patternklase ay may iba pang mga pamamaraan para sa pagtatrabaho sa mga regular na expression:
  • String pattern()‒ ibinabalik ang orihinal na representasyon ng string ng regular na expression na ginamit upang likhain ang Patternbagay:

    
    Pattern pattern = Pattern.compile("abc");
    System.out.println(pattern.pattern()); // "abc"
    
  • static boolean matches(String regex, CharSequence input)– hinahayaan kang suriin ang regular na expression na ipinasa bilang regex laban sa tekstong ipinasa bilang input. Ibinabalik:

    totoo - kung ang teksto ay tumutugma sa pattern;
    mali – kung hindi;

    Halimbawa:

    
    System.out.println(Pattern.matches("A.+a","Anna")); // true
    System.out.println(Pattern.matches("A.+a","Fred Anna Alexander")); // false
    
  • int flags()‒ ibinabalik ang halaga ng flagsset ng parameter ng pattern noong ginawa ang pattern o 0 kung hindi nakatakda ang parameter. Halimbawa:

    
    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)– hinahati ang naipasa na teksto sa isang Stringarray. Isinasaad ng limitparameter ang maximum na bilang ng mga tugmang hinanap sa text:

    • kung limit > 0limit-1tugma;
    • kung limit < 0‒ lahat ng tugma sa teksto
    • kung limit = 0‒ lahat ng tugma sa teksto, ang mga walang laman na string sa dulo ng array ay itatapon;

    Halimbawa:

    
    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 ng console:

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

    Sa ibaba ay isasaalang-alang natin ang isa pang pamamaraan ng klase na ginamit upang lumikha ng isang Matcherbagay.

Mga pamamaraan ng klase ng Matcher

Ang mga pagkakataon ng Matcherklase ay nilikha upang magsagawa ng pagtutugma ng pattern. Matcheray ang "search engine" para sa mga regular na expression. Upang magsagawa ng paghahanap, kailangan nating bigyan ito ng dalawang bagay: isang pattern at isang panimulang index. Upang lumikha ng isang Matcherbagay, ang Patternklase ay nagbibigay ng sumusunod na pamamaraan: рublic Matcher matcher(CharSequence input) Ang pamamaraan ay tumatagal ng isang pagkakasunud-sunod ng character, na hahanapin. Ito ay isang halimbawa ng isang klase na nagpapatupad ng CharSequenceinterface. Maaari kang makapasa hindi lamang sa isang String, kundi pati na rin sa isang StringBuffer, StringBuilder, Segment, o CharBuffer. Ang pattern ay isang Patternbagay kung saan matchertinatawag ang pamamaraan. Halimbawa ng paglikha ng isang matcher:

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"
Ngayon ay magagamit na namin ang aming "search engine" upang maghanap ng mga tugma, makuha ang posisyon ng isang tugma sa teksto, at palitan ang teksto gamit ang mga pamamaraan ng klase. Hinahanap ng boolean find()pamamaraan ang susunod na tugma sa teksto. Maaari naming gamitin ang paraang ito at isang loop na pahayag upang suriin ang isang buong teksto bilang bahagi ng isang modelo ng kaganapan. Sa madaling salita, maaari kaming magsagawa ng mga kinakailangang operasyon kapag naganap ang isang kaganapan, ibig sabihin, kapag nakakita kami ng isang tugma sa teksto. Halimbawa, maaari naming gamitin ang klase int start()at int end()mga pamamaraan na ito upang matukoy ang posisyon ng isang tugma sa teksto. At maaari naming gamitin ang String replaceFirst(String replacement)at String replaceAll(String replacement)mga pamamaraan upang palitan ang mga tugma sa halaga ng kapalit na parameter. Halimbawa:

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);
}
Output:

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
Nilinaw ng halimbawa na ang replaceFirstat replaceAllmga pamamaraan ay lumilikha ng isang bagong Stringbagay — isang string kung saan ang pattern ay tumutugma sa orihinal na teksto ay pinapalitan ng tekstong ipinasa sa pamamaraan bilang isang argumento. Bukod pa rito, replaceFirstpinapalitan lamang ng pamamaraan ang unang tugma, ngunit replaceAllpinapalitan ng pamamaraan ang lahat ng tugma sa teksto. Ang orihinal na teksto ay nananatiling hindi nagbabago. Ang Patternat Matchermga klase ng pinakamadalas na pagpapatakbo ng regex ay binuo mismo sa Stringklase. Ito ay mga pamamaraan tulad ng split, matches, replaceFirst, at replaceAll. Ngunit sa ilalim ng hood, ang mga pamamaraang ito ay gumagamit ng Patternat Matchermga klase. Kaya kung gusto mong palitan ang text o ihambing ang mga string sa isang programa nang hindi nagsusulat ng anumang karagdagang code, gamitin ang mga pamamaraan ngStringklase. Kung kailangan mo ng mas advanced na mga tampok, tandaan ang Patternat Matchermga klase.

Konklusyon

Sa isang Java program, ang isang regular na expression ay tinutukoy ng isang string na sumusunod sa mga partikular na panuntunan sa pagtutugma ng pattern. Kapag nag-execute ng code, kino-compile ng Java machine ang string na ito sa isang Patternobject at gumagamit ng Matcherobject para maghanap ng mga tugma sa text. Tulad ng sinabi ko sa simula, madalas na ipinagpaliban ng mga tao ang mga regular na ekspresyon para sa ibang pagkakataon, na isinasaalang-alang ang mga ito na isang mahirap na paksa. Ngunit kung nauunawaan mo ang pangunahing syntax, metacharacter, at pagtakas ng character, at pag-aralan ang mga halimbawa ng mga regular na expression, makikita mong mas simple ang mga ito kaysa sa unang tingin.

Higit pang pagbabasa: