Düzenli ifadeler, programcıların, hatta deneyimli olanların bile çoğu zaman sonraya ertelediği bir konudur. Ancak er ya da geç, çoğu Java geliştiricisi metin bilgilerini işlemek zorunda kalır. Çoğu zaman bu, metin arama ve düzenleme anlamına gelir. Düzenli ifadeler olmadan etkili ve kompakt metin işleme kodu düşünülemez. O halde ertelemeyi bırakın, hemen normal ifadelerle ilgilenelim. O kadar zor değil. Java'da düzenli ifadeler - 1

Düzenli ifade (normal ifade) nedir?

Aslında, bir düzenli ifade, metinde bir dize bulmak için kullanılan bir kalıptır. Java'da, bu kalıbın orijinal gösterimi her zaman bir dizgedir, yani sınıfın bir nesnesidir String. Ancak, normal bir ifadede derlenebilen herhangi bir dize değildir; yalnızca düzenli ifadeler oluşturma kurallarına uyan dizelerdir. Sözdizimi, dil belirtiminde tanımlanır. Normal ifadeler, normal ifade sözdiziminde özel anlamı olan karakterler olan meta karakterlerin yanı sıra harfler ve sayılar kullanılarak yazılır. Örneğin:

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

Java'da düzenli ifadeler oluşturma

Java'da düzenli bir ifade oluşturmak iki basit adımı içerir:
  1. normal ifade sözdizimine uyan bir dize olarak yazın;
  2. dizgiyi düzenli bir ifadede derleyin;
Herhangi bir Java programında, bir Patternnesne oluşturarak düzenli ifadelerle çalışmaya başlarız. Bunu yapmak için, sınıfın iki statik yönteminden birini çağırmamız gerekir: compile. İlk yöntem bir bağımsız değişken (düzenli ifadeyi içeren bir dize hazır değeri) alırken, ikincisi kalıp eşleştirme ayarlarını belirleyen ek bir bağımsız değişken alır:

public static Pattern compile (String literal)
public static Pattern compile (String literal, int flags)
Parametrenin potansiyel değerlerinin listesi sınıfta flagstanımlanır Patternve bize statik sınıf değişkenleri olarak sunulur. Örneğin:

Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE); // Pattern-matching will be case insensitive.
Temel olarak Patternsınıf, düzenli ifadeler için bir kurucudur. Başlık altında, compileyöntem, derlenmiş bir temsil oluşturmak için sınıfın özel kurucusunu çağırır Pattern. Bu nesne yaratma mekanizması değişmez nesneler yaratmak için bu şekilde uygulanır. Normal bir ifade oluşturulduğunda sözdizimi kontrol edilir. Dize hatalar içeriyorsa, o zaman bir PatternSyntaxExceptionoluşturulur.

Normal ifade sözdizimi

<([{\^-=$!|]})?*+.>Normal ifade sözdizimi , harflerle birleştirilebilen karakterlere dayanır . Rollerine bağlı olarak, birkaç gruba ayrılabilirler:
1. Satırların veya metnin sınırlarını eşleştirmek için meta karakterler
Meta karakter Tanım
^ bir satırın başlangıcı
$ bir satırın sonu
\B kelime sınırı
\B sözcük olmayan sınır
\A girişin başlangıcı
\G önceki maçın sonu
\Z girişin sonu
\z girişin sonu
2. Önceden tanımlanmış karakter sınıflarını eşleştirmek için meta karakterler
Meta karakter Tanım
\D hane
\D rakamsız
\S boşluk karakteri
\S boşluk olmayan karakter
\w alfasayısal karakter veya alt çizgi
\W harfler, sayılar ve alt çizgi dışında herhangi bir karakter
. herhangi bir karakter
3. Eşleşen kontrol karakterleri için meta karakterler
Meta karakter Tanım
\T sekme karakteri
\N yeni satır karakteri
\R satırbaşı
\F satır besleme karakteri
\u0085 sonraki satır karakteri
\u2028 satır ayırıcı
\u2029 paragraf ayırıcı
4. Karakter sınıfları için meta karakterler
Meta karakter Tanım
[ABC] listelenen karakterlerden herhangi biri (a, b veya c)
[^abc] listelenenler dışında herhangi bir karakter (a, b veya c değil)
[a-zA-Z] birleştirilmiş aralıklar (a'dan z'ye Latin karakterleri, büyük/küçük harf duyarlı değildir)
[reklam[mp]] karakter birliği (a'dan d'ye ve m'den p'ye)
[az&&[tanım]] karakterlerin kesişimi (d, e, f)
[az&&[^bc]] karakterlerin çıkarılması (a, dz)
5. Karakter sayısını (nicelik belirteçleri) belirtmek için meta karakterler. Bir niceleyiciden önce her zaman bir karakter veya karakter grubu gelir.
Meta karakter Tanım
? biri ya da hiçbiri
* sıfır veya daha fazla kez
+ bir veya daha fazla kez
{N} n kez
{N,} n veya daha fazla kez
{n,m} en az n kez ve en fazla m kez

Açgözlü niceleyiciler

Nicelik belirteçleri hakkında bilmeniz gereken bir şey, bunların üç farklı çeşidi olduğudur: açgözlü, sahiplenici ve isteksiz. +Niceleyiciden sonra bir " " karakteri ekleyerek bir niceleyiciyi iyelik haline getirirsiniz . " " ekleyerek isteksiz hale getiriyorsunuz ?. Örneğin:

"A.+a" // greedy
"A.++a" // possessive
"A.+?a" // reluctant
Farklı niceleyici türlerinin nasıl çalıştığını anlamak için bu modeli kullanmayı deneyelim. Varsayılan olarak, niceleyiciler açgözlüdür. Bu, dizideki en uzun eşleşmeyi aradıkları anlamına gelir. Aşağıdaki kodu çalıştırırsak:

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()));
    }
}
şu çıktıyı alıyoruz:

Anna Alexa
" " normal ifadesi için A.+akalıp eşleştirme şu şekilde gerçekleştirilir:
  1. Belirtilen kalıptaki ilk karakter Latin harfidir A. Matchersıfır dizininden başlayarak metnin her karakteriyle karşılaştırır. Karakter, Fmetnimizde sıfır dizinindedir, bu nedenle, Matcherkalıpla eşleşene kadar karakterler arasında yinelenir. Örneğimizde, bu karakter 5. indekste bulunur.

    Java'da düzenli ifadeler - 2
  2. Modelin ilk karakteri ile bir eşleşme bulunduğunda, Matcherikinci karakteri ile bir eşleşme arar. Bizim durumumuzda, .herhangi bir karakteri temsil eden " " karakteridir.

    Java'da düzenli ifadeler - 3

    Karakter naltıncı konumdadır. Kesinlikle "herhangi bir karakter" için bir eşleşme olarak nitelendirilir.

  3. Matcherdesenin bir sonraki karakterini kontrol etmeye devam eder. Bizim kalıbımızda, önceki karaktere uygulanan niceleyiciye dahil edilmiştir: " .+". Örüntümüzdeki "herhangi bir karakter"in tekrar sayısı bir veya daha fazla olduğu için, Matcherart arda diziden bir sonraki karakteri alır ve "herhangi bir karakter" ile eşleştiği sürece kalıpla karşılaştırır. Örneğimizde — dizenin sonuna kadar (7. dizinden 18. dizine).

    Java'da düzenli ifadeler - 4

    Temel olarak, Matcheripi sonuna kadar yutar - "açgözlü" ile kastedilen tam olarak budur.

  4. Matcher metnin sonuna ulaştıktan ve A.+kalıbın " " kısmı için kontrolü bitirdikten sonra, kalıbın geri kalanını kontrol etmeye başlar: a. İleriye giden başka metin yok, bu nedenle kontrol, son karakterden başlayarak "geri çekilerek" devam ediyor:

    Java'da düzenli ifadeler - 5
  5. Matcher.+kalıbın " " kısmındaki tekrar sayısını "hatırlar" . Bu noktada, tekrar sayısını bir azaltır ve bir eşleşme bulunana kadar daha büyük kalıbı metne göre kontrol eder:

    Java'da düzenli ifadeler - 6

İyelik niceleyicileri

İyelik niceleyicileri, açgözlü niceleyicilere çok benzer. Aradaki fark, metin dizenin sonuna kadar yakalandığında, "geri çekilirken" kalıp eşleştirme olmamasıdır. Başka bir deyişle, ilk üç aşama açgözlü niceleyicilerle aynıdır. Dizinin tamamını yakaladıktan sonra eşleştirici, desenin geri kalanını düşündüğü şeye ekler ve onu yakalanan diziyle karşılaştırır. Örneğimizde, " A.++a" normal ifadesini kullanan ana yöntem hiçbir eşleşme bulamıyor. Java'da düzenli ifadeler - 7

isteksiz niceleyiciler

  1. Açgözlü çeşitlilikte olduğu gibi, bu niceleyiciler için kod, modelin ilk karakterine dayalı bir eşleşme arar:

    Java'da düzenli ifadeler - 8
  2. Ardından, kalıbın bir sonraki karakteriyle (herhangi bir karakter) bir eşleşme arar:

    Java'da düzenli ifadeler - 9
  3. Açgözlü kalıp eşleştirmenin aksine, gönülsüz kalıp eşleştirmede en kısa eşleşme aranır. Bu, kalıbın ikinci karakteriyle (metindeki 6. konumdaki karaktere karşılık gelen bir nokta) bir eşleşme bulduktan sonra, Matchermetnin kalıbın geri kalanıyla - " a" karakteriyle eşleşip eşleşmediğini kontrol ettiği anlamına gelir.

    Java'da düzenli ifadeler - 10
  4. Metin kalıpla eşleşmiyor (yani n7. indekste " " karakterini içeriyor), bu nedenle Matchernicelik belirteci bir veya daha fazlasını gösterdiği için birden fazla "herhangi bir karakter" ekler. Ardından, deseni 5'ten 8'e kadar olan konumlardaki metinle tekrar karşılaştırır:

    Java'da düzenli ifadeler - 11
  5. Bizim durumumuzda bir eşleşme bulundu, ancak henüz metnin sonuna gelmedik. Bu nedenle örüntü eşleştirme 9. konumdan yeniden başlar, yani örüntünün ilk karakteri benzer bir algoritma kullanılarak aranır ve bu, metnin sonuna kadar tekrarlanır.

    Java'da düzenli ifadeler - 12
Buna göre yöntem, main" " kalıbını kullanırken aşağıdaki sonucu elde eder A.+?a: Anna Alexa Örneğimizden de görebileceğiniz gibi, farklı tip niceleyiciler aynı kalıp için farklı sonuçlar üretir. Bu yüzden bunu aklınızda bulundurun ve aradığınız şeye göre doğru çeşidi seçin.

Normal ifadelerde kaçan karakterler

Java'daki bir düzenli ifade veya daha doğrusu orijinal gösterimi bir dizge değişmez değeri olduğundan, dize değişmezleriyle ilgili Java kurallarını hesaba katmamız gerekir. Özellikle, \Java kaynak kodundaki dizge değişmezlerindeki " " ters eğik çizgi karakteri, derleyiciye bir sonraki karakterin özel olduğunu ve özel bir şekilde yorumlanması gerektiğini söyleyen bir kontrol karakteri olarak yorumlanır. Örneğin:

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"
Bu, normal ifadeleri tanımlayan ve " \" karakterlerini (yani metakarakterleri belirtmek için) kullanan dize hazır değerlerinin, Java bayt kodu derleyicisinin dizeyi yanlış yorumlamamasını sağlamak için ters eğik çizgileri tekrar etmesi gerektiği anlamına gelir. Örneğin:

String regex = "\\s"; // Pattern for matching a whitespace character
String regex = "\"Windows\"";  // Pattern for matching "Windows"
"Normal" karakterler olarak kullanmak istediğimiz özel karakterlerden kaçmak için çift ters eğik çizgi de kullanılmalıdır. Örneğin:

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

Pattern sınıfının yöntemleri

Sınıfın Patternnormal ifadelerle çalışmak için başka yöntemleri vardır:
  • String pattern()‒ nesneyi oluşturmak için kullanılan normal ifadenin orijinal dize gösterimini döndürür Pattern:

    
    Pattern pattern = Pattern.compile("abc");
    System.out.println(pattern.pattern()); // "abc"
    
  • static boolean matches(String regex, CharSequence input)– normal ifade olarak iletilen normal ifadeyi, olarak iletilen metne karşı kontrol etmenizi sağlar input. İadeler:

    true - metin kalıpla eşleşirse;
    false - değilse;

    Örneğin:

    
    System.out.println(Pattern.matches("A.+a","Anna")); // true
    System.out.println(Pattern.matches("A.+a","Fred Anna Alexander")); // false
    
  • int flags()‒ model oluşturulduğunda modelin flagsparametre seti değerini veya parametre ayarlanmamışsa 0 değerini döndürür. Örneğin:

    
    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)– geçirilen metni bir diziye böler String. Parametre limit, metinde aranan maksimum eşleşme sayısını gösterir:

    • eğer limit > 0limit-1eşleşirse;
    • eğer limit < 0- metindeki tüm eşleşmeler
    • eğer limit = 0‒ metindeki tüm eşleşmeler, dizinin sonundaki boş dizeler atılırsa;

    Örneğin:

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

    Konsol çıktısı:

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

    Aşağıda, sınıfın bir Matchernesne oluşturmak için kullanılan başka bir yöntemini ele alacağız.

Matcher sınıfının yöntemleri

Sınıfın örnekleri, Matcherdesen eşleştirmeyi gerçekleştirmek için oluşturulur. Matcherdüzenli ifadeler için "arama motorudur". Bir arama yapmak için ona iki şey vermemiz gerekir: bir model ve bir başlangıç ​​dizini. MatcherBir nesne yaratmak için , Patternsınıf aşağıdaki yöntemi sağlar: рublic Matcher matcher(CharSequence input) Yöntem, aranacak olan bir karakter dizisini alır. Bu, arabirimi uygulayan bir sınıfın örneğidir CharSequence. StringYalnızca a'yı değil , aynı zamanda StringBuffer, StringBuilder, Segmentveya'yı da iletebilirsiniz CharBuffer. Desen, yöntemin çağrıldığı bir Patternnesnedir matcher. Eşleştirici oluşturma örneği:

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"
Artık eşleşmeleri aramak, metindeki bir eşleşmenin konumunu almak ve sınıfın yöntemlerini kullanarak metni değiştirmek için "arama motorumuzu" kullanabiliriz. Yöntem boolean find(), metindeki bir sonraki eşleşmeyi arar. Metnin tamamını bir olay modelinin parçası olarak analiz etmek için bu yöntemi ve bir döngü deyimini kullanabiliriz. Yani bir olay meydana geldiğinde yani metinde bir eşleşme bulduğumuzda gerekli işlemleri yapabiliyoruz. Örneğin, bir eşleşmenin metindeki konumunu belirlemek için bu sınıfın int start()ve yöntemlerini kullanabiliriz . Eşleşmeleri değiştirme parametresinin değeriyle değiştirmek için ve yöntemlerini int end()kullanabiliriz . Örneğin: String replaceFirst(String replacement)String replaceAll(String replacement)

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);
}
Çıktı:

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
Örnek, replaceFirstve replaceAllyöntemlerinin yeni bir Stringnesne - orijinal metindeki kalıp eşleşmelerinin, yönteme bağımsız değişken olarak iletilen metinle değiştirildiği bir dize - yarattığını açıkça ortaya koymaktadır. Ayrıca, replaceFirstyöntem yalnızca ilk eşleşmeyi değiştirir, ancak replaceAllyöntem metindeki tüm eşleşmeleri değiştirir. Orijinal metin değişmeden kalır. ve sınıflarının en sık düzenli ifade işlemleri doğrudan sınıfın içine yerleştirilmiştir Pattern. Bunlar , , ve gibi yöntemlerdir . Ancak, bu yöntemler, gizli olarak ve sınıflarını kullanır. Bu nedenle, herhangi bir ekstra kod yazmadan bir programdaki metni değiştirmek veya dizeleri karşılaştırmak istiyorsanız, aşağıdaki yöntemleri kullanın:MatcherStringsplitmatchesreplaceFirstreplaceAllPatternMatcherStringsınıf. Daha gelişmiş özelliklere ihtiyacınız varsa, Patternve Matchersınıflarını unutmayın.

Çözüm

Bir Java programında, düzenli bir ifade, belirli kalıp eşleştirme kurallarına uyan bir dize tarafından tanımlanır. Kod yürütülürken, Java makinesi bu diziyi bir nesnede derler Patternve Matchermetindeki eşleşmeleri bulmak için bir nesne kullanır. Başta da söylediğim gibi, insanlar genellikle normal ifadeleri zor bir konu olarak gördükleri için sonraya erteliyorlar. Ancak temel sözdizimini, meta karakterleri ve karakter kaçışını anlarsanız ve normal ifadelerin örneklerini incelerseniz, bunların ilk bakışta göründüklerinden çok daha basit olduğunu göreceksiniz.

Daha fazla okuma: