नियमित अभिव्यक्ति का उपयोग करके बहुस्तरीय पाठ का मिलान करें


174

मैं जावा का उपयोग करते हुए एक मल्टी लाइन टेक्स्ट से मिलान करने की कोशिश कर रहा हूं। जब मैं संशोधक के Patternसाथ कक्षा का उपयोग करता Pattern.MULTILINEहूं, तो मैं मिलान करने में सक्षम हूं, लेकिन मैं ऐसा करने में सक्षम नहीं हूं(?m).

एक ही पैटर्न के साथ (?m)और उपयोग String.matchesकरने के लिए काम नहीं लगता है।

मुझे यकीन है कि मुझे कुछ याद आ रहा है, लेकिन पता नहीं क्या। नियमित अभिव्यक्तियों में बहुत अच्छा नहीं है।

यही मैंने कोशिश की

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?

जवाबों:


298

सबसे पहले, आप एक गलत धारणा के तहत संशोधक का उपयोग कर रहे हैं।

Pattern.MULTILINEया (?m)जावा बताता एंकर स्वीकार करने के लिए ^और $शुरू में मैच के लिए और प्रत्येक पंक्ति के अंत (अन्यथा वे केवल पूरी स्ट्रिंग की शुरुआत / अंत में मेल खाते हैं)।

Pattern.DOTALLया (?s)जावा से कहता है कि डॉट को भी नईलाइन वर्णों का मिलान करने की अनुमति दें।

दूसरे, आपके मामले में, रेगेक्स विफल हो जाता है क्योंकि आप उस matches()विधि का उपयोग कर रहे हैं जो रेगेक्स से पूरी स्ट्रिंग का मिलान करने की अपेक्षा करता है - जो निश्चित रूप से काम नहीं करता है क्योंकि (\\W)*(\\S)*मिलान होने के बाद कुछ अक्षर बचे हैं।

तो अगर आप बस एक स्ट्रिंग की तलाश कर रहे हैं User Comments:, जो शुरू होता है , तो रेगेक्स का उपयोग करें

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

Pattern.DOTALLविकल्प के साथ :

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

ResultString इसके बाद पाठ शामिल होगा User Comments:


मैं एक ऐसा पैटर्न खोजने की कोशिश कर रहा हूं, जो "उपयोगकर्ता टिप्पणियाँ:" के साथ शुरू होने वाले किसी भी स्ट्रिंग से मेल खाएगा। इसके बाद "उपयोगकर्ता टिप्पणियाँ:" एक उपयोगकर्ता एक textarea में प्रवेश करती है, और इसलिए कुछ भी हो सकता है - यहां तक ​​कि नई लाइनें भी। लगता है मुझे रेगेक्स में बहुत कुछ सीखने की ज़रूरत है ...
निवास

2
यह काम करता है (धन्यवाद!) मैंने पैटर्न की कोशिश की (?s)User Comments:\s*(.*)। @Amarghosh के जवाब से मुझे पैटर्न मिला User Comments: [\\s\\S]*। इनमें से एक बेहतर या अनुशंसित तरीका है या ये एक ही करने के दो अलग-अलग तरीके हैं?
निवास

3
वे दोनों का अर्थ समान है; [\s\S]है थोड़ा अधिक स्पष्ट ( "किसी भी चरित्र है कि या तो खाली स्थान या गैर-सफ़ेद से मेल खाते हैं"), .पढ़ने के लिए आसान है, लेकिन आप के लिए देखने की जरूरत है (?s)या DOTALLआदेश पता लगाने के लिए कि क्या नई पंक्तियां शामिल किए गए हैं या नहीं में संशोधक। मैं ध्वज सेट के .साथ पसंद करूंगा Pattern.DOTALL(यह (?s)मेरी राय में पढ़ने और याद रखने में आसान है । आपको उस चीज का उपयोग करना चाहिए जो आपको सबसे अधिक आरामदायक लगता है।
टिम पीटरसन

.*साथ DOTALLअधिक पठनीय है। मैंने अन्य एक का उपयोग यह दिखाने के लिए किया कि यह मुद्दा स्ट्रैचचेज़ और मैचर.फंड के बीच अंतर में है न कि झंडे के। +1
अमरघोष

मैं पसंद .*के साथ Pattern.DOTALLहै, लेकिन साथ जाना होगा (? एस) क्योंकि मैं का उपयोग करना होगा String.matches
निवास

42

इसका MULTILINE ध्वज से कोई लेना-देना नहीं है; आप जो देख रहे हैं, वह find()और matches()विधियों के बीच का अंतर है । find()सफल होता है कि मैच स्ट्रिंग स्ट्रिंग में कहीं भी पाया जा सकता है , जबकि पूरे स्ट्रिंगmatches() से मिलान करने के लिए रेगेक्स की अपेक्षा करता है ।

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

इसके अलावा, MULTILINEइसका मतलब यह नहीं है कि आप क्या सोचते हैं। बहुत से लोग इस निष्कर्ष पर कूदते हैं कि आपको उस झंडे का उपयोग करना है यदि आपके लक्ष्य स्ट्रिंग में नई सुर्खियाँ हैं - अर्थात्, यदि इसमें कई तार्किक रेखाएँ हैं। मैंने एसओ से उस प्रभाव के बारे में कई जवाब यहां देखे हैं, लेकिन वास्तव में, वह सब जो झंडा करता है वह एंकरों के व्यवहार को बदल देता है, ^और $

आम तौर पर ^लक्ष्य स्ट्रिंग की बहुत शुरुआत से $मेल खाता है , और बहुत अंत से मेल खाता है (या अंत में एक नई रेखा से पहले, लेकिन हम अभी के लिए एक तरफ छोड़ देंगे)। लेकिन अगर स्ट्रिंग नई पंक्तियां शामिल है, आप के लिए चुन सकते हैं ^और $, शुरू में मेल खाते हैं और MULTILINE झंडा निर्धारित करके किसी भी तार्किक लाइन, न सिर्फ शुरुआत है और पूरे स्ट्रिंग के अंत के अंत तक।

तो MULTILINE इसका क्या मतलब है और यह याद रखें कि यह क्या करता है : एंकर ^और $एंकर का व्यवहार बदल जाता है । DOTALLमोड को मूल रूप से "सिंगल-लाइन" कहा जाता था (और अभी भी पर्ल और .NET सहित कुछ स्वादों में है), और इसने हमेशा बहुत भ्रम पैदा किया है। हम भाग्यशाली हैं कि जावा देवता उस मामले में अधिक वर्णनात्मक नाम के साथ गए, लेकिन "मल्टीलाइन" मोड के लिए कोई उचित विकल्प नहीं था।

पर्ल में, जहां यह सब पागलपन शुरू हुआ, उन्होंने अपनी गलती स्वीकार की है और पर्ल 6 रेगेक्स में "मल्टीलाइन" और "सिंगल-लाइन" दोनों तरीकों से छुटकारा पा लिया है। एक और बीस वर्षों में, शायद दुनिया के बाकी हिस्सों ने सूट का पालन किया होगा।


5
यह विश्वास करना मुश्किल है कि उन्होंने "#matches" विधि नाम का उपयोग किया है जिसका अर्थ है "सभी"
बाइक

@ एलन-मूर क्षमा करें मैं इसे नीचे कर देता हूं, भले ही यह सही हो [अधिक नींद की जरूरत है]]
रेमंड नसेफ

22

str.matches(regex) व्यवहार करता है Pattern.matches(regex, str) जो पैटर्न और रिटर्न के खिलाफ पूरे इनपुट अनुक्रम से मेल खाने का प्रयास करता है

trueयदि, और केवल यदि, संपूर्ण इनपुट अनुक्रम इस मिलानकर्ता के पैटर्न से मेल खाता है

जबकि पैटर्न और रिटर्न से मेल खाने वाले इनपुट अनुक्रम के अगले बाद का matcher.find() पता लगाने का प्रयास करता है

trueयदि, और केवल तभी, इनपुट अनुक्रम की एक अनुवर्तीता इस मिलानकर्ता के पैटर्न से मेल खाती है

इस प्रकार समस्या regex के साथ है। निम्नलिखित प्रयास करें।

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

इस प्रकार संक्षेप में, (\\W)*(\\S)*आपके पहले रेगेक्स में भाग एक रिक्त स्ट्रिंग से मेल खाता है जैसा *कि शून्य या अधिक घटनाएँ हैं और वास्तविक मिलान स्ट्रिंग है User Comments:और पूरे स्ट्रिंग नहीं है जैसा कि आप अपेक्षा करेंगे। दूसरा एक विफल हो जाता है क्योंकि यह पूरे स्ट्रिंग से मेल खाने की कोशिश करता है, लेकिन यह \\Wएक गैर शब्द चरित्र से मेल नहीं खाता है, [^a-zA-Z0-9_]और पहला चरित्र Tएक शब्द चरित्र है।


मैं किसी भी स्ट्रिंग का मिलान करना चाहता हूं जो "उपयोगकर्ता टिप्पणियाँ" से शुरू होती है, और स्ट्रिंग में नए अंक भी हो सकते हैं। इसलिए मैंने पैटर्न का उपयोग किया User Comments: [\\s\\S]*और यह काम किया। (धन्यवाद!) @Tim के उत्तर से मुझे पैटर्न मिल गया User Comments:(.*), यह भी ठीक है। अब, क्या इनमें से एक अनुशंसित या बेहतर तरीका है, या ये केवल दो तरीके हैं?
निवास

@ निवाज़ मुझे नहीं लगता कि कोई अंतर प्रदर्शन बुद्धिमान होगा; लेकिन मुझे लगता है कि ध्वज (.*)के साथ-साथ DOTALLस्पष्ट / पठनीय है([\\s\\S]*)
अमरघोष

यह सबसे अच्छा उत्तर है .... मल्टीलाइन क्षमता के लिए जावा कोड और पैटर्न स्ट्रिंग दोनों विकल्पों तक पहुँच प्रदान करता है।
गोल्डबिशप

0

मल्टीलाइन ध्वज रेगेक्स को प्रत्येक पंक्ति के पैटर्न से मेल खाने के लिए कहता है क्योंकि आपके उद्देश्यों के लिए पूरे स्ट्रिंग का विरोध एक जंगली कार्ड पर्याप्त होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.