2016-01-14 9 views
11

पर विचार मैं की तरह एक फ़ाइल (सिर्फ एक अंश)जावा के साथ फाइलों में पैटर्न का पता लगाएं 8

name: 'foobar' 

मैं जब मैं name के साथ लाइन की खोज foobar पुनः प्राप्त करना चाहते है।

मेरे वर्तमान दृष्टिकोण

Pattern m = Pattern.compile("name: '(.+)'"); 
try (Stream<String> lines = Files.lines(ruleFile)) { 
    Optional<String> message = lines.filter(m.asPredicate()).findFirst(); 
    if (message.isPresent()) { 
     Matcher matcher = m.matcher(message.get()); 
     matcher.find(); 
     String group = matcher.group(1); 
     System.out.println(group); 
    } 
} 

जो अच्छा नहीं दिखता है। पैटर्न और matcher का अत्यधिक उपयोग गलत लगता है।

क्या कोई आसान/बेहतर तरीका है? विशेष रूप से यदि मेरे पास एकाधिक कुंजी हैं तो मुझे इस तरह खोजना पसंद है?

उत्तर

21

मैं और अधिक कुछ इस तरह उम्मीद होती है, पैटर्न दो बार मिलान से बचने के लिए:

Pattern p = Pattern.compile("name: '([^']*)'"); 
lines.map(p::matcher) 
    .filter(Matcher::matches) 
    .findFirst() 
    .ifPresent(matcher -> System.out.println(matcher.group(1))); 

यही है, प्रत्येक स्ट्रिंग के मिलान के लिए, पहले एक से मेल खाता है, कि एक प्रिंट आउट पहले समूह के लिए मिलता है।

Matcher m = Pattern.compile("name: '(.+)'").matcher(""); 
try(Stream<String> lines = Files.lines(ruleFile)) { 
    lines.flatMap(line -> m.reset(line).results().limit(1)) 
     .forEach(mr -> System.out.println(mr.group(1))); 
} 

यह विधि Matcher.results() जो सभी मैचों की एक धारा देता है का उपयोग करता है:

+0

'मानचित्र (पी :: matcher)' पाइपलाइन चरण प्रत्येक पंक्ति के लिए एक नया 'Matcher' ऑब्जेक्ट बनाता है जो पढ़ने के लिए है। बहुत बड़ी फ़ाइलों के लिए, यह अक्षमता का स्रोत हो सकता है। इसके बजाए, एक पुन: प्रयोज्य 'Matcher' ऑब्जेक्ट बनाया जा सकता है, और' .map (matcher :: reset) 'का उपयोग लाइन से मैप करने के लिए किया जा सकता है, जिसे केवल पुन: प्रयोज्य 'matcher' में पढ़ा गया है, जैसा कि दिखाया गया है [https]//stackoverflow.com/a/47877960/3690024)। स्ट्रीम पाइपलाइनों में राज्यव्यापी वस्तुओं का पुन: उपयोग करने से सभी प्रकार के नियमों का उल्लंघन होता है, इसलिए यदि आप बड़ी फाइलें पढ़ रहे हैं, तो यह केवल इसकी सिफारिश करेगा, और यह एक बाधा बनने के लिए निर्धारित करें। – AJNeufeld

7

यह जावा 9 समाधान सबसे अधिक संभावना कैसा दिखाई देगा की तरह है। flatMap के माध्यम से मैचों की स्ट्रीम के साथ लाइनों की एक धारा का संयोजन करने से हम फ़ाइल के सभी मैचों को संसाधित कर सकते हैं। चूंकि आपका मूल कोड केवल एक पंक्ति के पहले मैच को संसाधित करता है, इसलिए मैंने एक ही व्यवहार प्राप्त करने के लिए प्रत्येक पंक्ति के मैचों में limit(1) जोड़ा।

दुर्भाग्यवश, यह विशेषता जावा 8 में याद आ रही है, हालांकि, आगामी विज्ञप्ति में चुपके एक विचार हो रही मदद करता है की तरह एक अंतरिम समाधान लग सकता है कैसे:

Matcher m = Pattern.compile("name: '(.+)'").matcher(""); 
try(Stream<String> lines = Files.lines(ruleFile)) { 
    lines.flatMap(line -> m.reset(line).find()? Stream.of(m.toMatchResult()): null) 
     .forEach(mr -> System.out.println(mr.group(1))); 
} 

उप धारा निर्माण आसान बनाने के लिए, इस समाधान का इस्तेमाल करता है कि केवल पहला मैच इरादा है और पहले स्थान पर एकल तत्व स्ट्रीम बनाता है।

लेकिन ध्यान दें कि प्रश्न के पैटर्न 'name: '(.+)' के साथ यह है कि क्या हम लालच से पिछले अनुवर्ती पंक्ति का ' अप करने के लिए सभी पात्रों से मेल खाएगी .+ के रूप में मैचों की संख्या को सीमित कोई फर्क नहीं पड़ता है, तो एक और मैच असंभव है। हालात अलग है जब name: '(.*?)' साथ की तरह एक अनिच्छुक परिमाणक जो बल्कि पिछले एक से अगले' अप करने के लिए खपत का उपयोग कर या, पिछले ' स्पष्ट रूप से छोड़ने के लिए मना name: '([^']*)' साथ के रूप में कर रहे हैं।


उपयोग एक साझा Matcher जो एकल पिरोया उपयोग के साथ अच्छी तरह से काम करता है (और यह कभी समानांतर प्रसंस्करण से लाभ होने की संभावना नहीं है) इसके बाद के संस्करण समाधान। लेकिन अगर आप धागा सुरक्षित पक्ष पर होना चाहता हूँ, आप केवल एक Pattern का हिस्सा है और इसके बजाय m.reset(line) बुलाने की एक Matcher बना सकते हैं:

Pattern pattern = Pattern.compile("name: '(.*)'"); 
try(Stream<String> lines = Files.lines(ruleFile)) { 
    lines.flatMap(line -> pattern.matcher(line).results().limit(1)) 
     .forEach(mr -> System.out.println(mr.group(1))); 
} 

resp। जावा 8

try(Stream<String> lines = Files.lines(ruleFile)) { 
    lines.flatMap(line -> {Matcher m=pattern.matcher(line); 
          return m.find()? Stream.of(m.toMatchResult()): null;}) 
     .forEach(mr -> System.out.println(mr.group(1))); 
} 

जो स्थानीय चर के परिचय के कारण संक्षिप्त नहीं है।यह एक पूर्ववर्ती map आपरेशन से बचा जा सकता है, लेकिन जब हम इस बिंदु पर के रूप में लंबे होते हैं, हम केवल प्रत्येक पंक्ति में एक भी मैच के लिए प्रमुख के रूप में, हम एक flatMap तो जरूरत नहीं है:

try(Stream<String> lines = Files.lines(ruleFile)) { 
    lines.map(pattern::matcher).filter(Matcher::find) 
     .forEach(m -> System.out.println(m.group(1))); 
} 

प्रत्येक Matcher के बाद से एक बार एक गैर-हस्तक्षेप करने के तरीके में, एक बार उपयोग किया जाता है, इसकी परिवर्तनीय प्रकृति यहां चोट नहीं पहुंची है और एक अपरिवर्तनीय MatchResult में रूपांतरण अनावश्यक हो जाता है।

हालांकि, इन समाधान बार बार एक नया Matcher वस्तु बनाने में, प्रत्येक पंक्ति में कई मैचों पर कार्रवाई करने के अगर वह कभी आवश्यक हो जाता है छोटा नहीं किया जा सकता ...

0

जवाब @khelwood परिणामों से है, जो हो सकता है लंबी फ़ाइलों को स्कैन किए जाने पर अक्षमता का स्रोत।

निम्न समाधान केवल एक बार मैचर बनाता है, और फ़ाइल में प्रत्येक पंक्ति के लिए इसका पुन: उपयोग करता है।

Pattern p = Pattern.compile("name: '([^']*)'"); 
Matcher matcher = p.matcher(""); // Create a matcher for the pattern 

Files.lines(ruleFile) 
    .map(matcher::reset)   // Reuse the matcher object 
    .filter(Matcher::matches) 
    .findFirst() 
    .ifPresent(m -> System.out.println(m.group(1))); 

चेतावनी - संदिग्ध हैक आगे

.map(matcher::reset) पाइपलाइन चरण जहां जादू/हैक होता है। यह प्रभावी रूप से matcher.reset(line) पर कॉल करता है, जो फ़ाइल से केवल पढ़ने के लिए लाइन पर अगला मिलान करने के लिए matcher रीसेट करता है, और चेनिंग कॉल की अनुमति देने के लिए स्वयं लौटाता है। .map(...) धारा ऑपरेटर एक Matcher वस्तु को लाइन से मानचित्रण रूप में समझती है लेकिन वास्तविकता में, हम एक ही वस्तु matcher हर बार करने के लिए मानचित्रण रखने के लिए, यह दुष्प्रभाव के बारे में नियमों का उल्लंघन करने के लिए सभी प्रकार, आदि

बेशक

, समानांतर धाराओं के लिए उपयोग नहीं किया जा सकता है, लेकिन सौभाग्य से फ़ाइल से पढ़ना मूल रूप से अनुक्रमिक है।

हैक या अनुकूलन? मुझे लगता है कि ऊपर/नीचे वोट तय करेंगे।

संबंधित मुद्दे