Normal ifadeyi kullanarak çok satırlı metni eşleme


174

Java kullanarak çok satırlı bir metin eşleştirmek çalışıyorum. Ben kullandığınızda Patternile sınıf Pattern.MULTILINEdeğiştirici, ben maç mümkün, ama ben ile bunu yapmak mümkün değilim(?m).

Aynı desen (?m)ve kullanımı String.matchesişe yaramıyor gibi görünüyor.

Bir şey eksik olduğuma eminim, ama ne olduğu hakkında hiçbir fikrim yok. Düzenli ifadelerde pek iyi değilim.

Ben bunu denedim

String test = "User Comments: This is \t a\ta \n test \n\n message \n";

String pattern1 = "User Comments: (\\W)*(\\S)*";
Pattern p = Pattern.compile(pattern1, Pattern.MULTILINE);
System.out.println(p.matcher(test).find());  //true

String pattern2 = "(?m)User Comments: (\\W)*(\\S)*";
System.out.println(test.matches(pattern2));  //false - why?

Yanıtlar:


298

İlk olarak, değiştiricileri yanlış bir varsayım altında kullanıyorsunuz.

Pattern.MULTILINEveya (?m)Java'ya çapaları kabul etmesini ^ve $her satırın başında ve sonunda eşleşmesini söyler (aksi takdirde yalnızca tüm dizenin başında / sonunda eşleşir).

Pattern.DOTALLveya (?s)Java'ya noktanın yeni satır karakterleriyle eşleşmesine izin vermesini söyler.

İkincisi, sizin durumunuzda, normal ifade başarısız olur, çünkü matches()normal ifadenin tüm dizeyle eşleşmesini bekleyen yöntemi kullanıyorsunuz (\\W)*(\\S)*.

Yani, sadece başlayan bir dize arıyorsanız User Comments:, normal ifadeyi kullanın

^\s*User Comments:\s*(.*)

ile Pattern.DOTALLseçeneği:

Pattern regex = Pattern.compile("^\\s*User Comments:\\s+(.*)", Pattern.DOTALL);
Matcher regexMatcher = regex.matcher(subjectString);
if (regexMatcher.find()) {
    ResultString = regexMatcher.group(1);
} 

ResultString daha sonra metni içerecektir User Comments:


"Kullanıcı Yorumları:" ile başlayan herhangi bir dize eşleşecek bir desen bulmaya çalışıyorum. Bundan sonra "Kullanıcı Yorumları:", kullanıcının bir metin alanına girdiği ve bu nedenle her şeyi (hatta yeni satırları) içerebileceği bir şeydir . Normalde çok şey öğrenmem gerekiyor gibi görünüyor ...
Nivas

2
Bu işe yarıyor (teşekkürler!) Deseni denedim (?s)User Comments:\s*(.*). @Amarghosh'ın cevabından kalıbı aldım User Comments: [\\s\\S]*. Bunlar arasında daha iyi ya da önerilen bir yol var mı ya da bunlar aynı şeyi yapmanın sadece iki farklı yolu mu?
Nivas

3
İkisi de aynı anlama geliyor; [\s\S]biraz daha açıktır ("boşluk veya boşluk olmayan herhangi bir karakterle eşleş"), .okunması daha kolaydır, ancak yeni satırların dahil edilip edilmediğini öğrenmek için (?s)veya DOTALLdeğiştiriciyi aramanız gerekir . Bayrak seti .ile tercih ederim Pattern.DOTALL(bu (?s)benim görüşüme göre okumak ve hatırlamak daha kolaydır . En rahat hissettiğiniz şeyi kullanmalısınız.
Tim Pietzcker

.*ile DOTALLdaha okunabilir. Diğerini sorunun bayraklar değil str.matches ve matcher.find arasındaki farklarda olduğunu göstermek için kullandım. +1
Amarghosh

Tercihim .*ile Pattern.DOTALLben kullanmak zorunda çünkü (? S), ancak gitmek zorunda kalacak String.matches.
Nivas

42

Bunun MULTILINE bayrağıyla ilgisi yoktur; Gördüğünüz şey find()ve matches()yöntemleri arasındaki farktır . find()Bir eşleşme bulunabilir eğer başarılı yerde hedef dizesinde ise matches()eşleştirmek için regex beklediği tüm dizeyi .

Pattern p = Pattern.compile("xyz");

Matcher m = p.matcher("123xyzabc");
System.out.println(m.find());    // true
System.out.println(m.matches()); // false

Matcher m = p.matcher("xyz");
System.out.println(m.matches()); // true

Dahası, MULTILINEne düşündüğünüz anlamına gelmez. Birçok kişi, hedef dizeniz yeni satırlar içeriyorsa (yani birden çok mantıksal satır içeriyorsa) bu bayrağı kullanmanız gerektiği sonucuna atlamaktadır. Burada SO üzerinde bu yönde birkaç cevap gördüm, ama aslında, bu bayrağın yaptığı tek şey çapaların davranışını değiştirmek ^ve $.

Normalde ^hedef dizenin en başıyla $eşleşir ve en ucuyla eşleşir (veya sonunda bir yeni satırdan önce, ancak şimdilik bir kenara bırakacağız). Ancak dize yeni satırlar içeriyorsa , MULTILINE bayrağını ayarlayarak herhangi bir mantıksal satırın başlangıcında ve sonunda, tüm dizenin yalnızca başlangıcı ve sonunda değil, eşleştirilmesini ^ve $eşleşmesini seçebilirsiniz .

Peki unutmak MULTILINE demektir ve sadece hatırlamak yapar : davranışını değiştirir ^ve $çapa. DOTALLmodu başlangıçta "tek satır" olarak adlandırıldı (ve hala Perl ve .NET dahil olmak üzere bazı tatlarda) ve her zaman benzer karışıklığa neden oldu. Java geliştiricilerinin bu durumda daha açıklayıcı bir adla gittiği için şanslıyız, ancak "çok satırlı" mod için makul bir alternatif yoktu.

Tüm bu çılgınlığın başladığı Perl'de, hatalarını kabul ettiler ve Perl 6 regexes'teki hem "çok satırlı" hem de "tek satırlı" modlardan kurtuldular. Yirmi yıl sonra, belki de dünyanın geri kalanı buna uygun olacak.


5
"#Matches" yöntem adını "tüm" eşleşmeleriyle eşleştirmek için kullandıklarına inanmak zor
rogerdpack

@ alan-moore Üzgünüm, doğru olmasına rağmen bunu
düşürdüm

22

str.matches(regex) davranır gibi Pattern.matches(regex, str) olan girişimler desen ve geri dönüş karşı tüm giriş sırasının fazlarını eşlemesidir

trueyalnızca ve tüm giriş sırası bu eşleştiricinin deseniyle eşleşiyorsa

Oysa giriş dizisinin kalıp ve döndürmeyle eşleşen bir sonraki sırasını matcher.find() bulmaya çalışır.

trueBir, ancak ve ancak, eğer altdizi girdi dizisinin bu eşleştirici desenini maçları

Böylece sorun normaldir. Takip etmeyi dene.

String test = "User Comments: This is \t a\ta \ntest\n\n message \n";

String pattern1 = "User Comments: [\\s\\S]*^test$[\\s\\S]*";
Pattern p = Pattern.compile(pattern1, Pattern.MULTILINE);
System.out.println(p.matcher(test).find());  //true

String pattern2 = "(?m)User Comments: [\\s\\S]*^test$[\\s\\S]*";
System.out.println(test.matches(pattern2));  //true

Kısacası, (\\W)*(\\S)*ilk normal ifadenizdeki bölüm, *sıfır veya daha fazla oluşum anlamına gelen boş bir dize ile eşleşir ve gerçek eşleşen dize, User Comments:beklediğiniz gibi tüm dize değildir. İkincisi, tüm dizeyi eşleştirmeye çalışırken başarısız olur, ancak \\Wkelime olmayan bir karakterle eşleşemez , yani [^a-zA-Z0-9_]ilk karakter Tbir kelime karakteridir.


Ben "Kullanıcı Yorumları" ile başlayan herhangi bir dize maç istiyorum ve dize de satırsonu içerebilir. Bu yüzden deseni kullandım User Comments: [\\s\\S]*ve bu işe yaradı. (teşekkürler!) @Tim cevabından kalıbı aldım User Comments:(.*), bu da tamam Şimdi, bunlar arasında önerilen veya daha iyi bir yol var mı, yoksa bunlar sadece iki şekilde mi yapılıyor?
Nivas

@Nivas Ben akıllıca bir performans farkı olacağını sanmıyorum; ama bayrak (.*)ile birlikte DOTALLdaha açık / okunabilir olduğunu düşünüyorum([\\s\\S]*)
Amarghosh

Bu en iyi yanıttır .... MultiLine özelliği için Java koduna ve Pattern String seçeneklerine erişim sağlar.
GoldBishop

0

Çok satırlı bayrak, regex'e, bir joker kartın yeterli olacağı amaçlarla, dizeyi tüm dizenin aksine her bir satırla eşleştirmesini söyler.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.