2008-12-17 18 views
33

के बराबर जावा मैं PHP से जावा में एक एप्लिकेशन को स्थानांतरित करने की प्रक्रिया में हूं और कोड में नियमित अभिव्यक्तियों का भारी उपयोग होता है। मैं PHP में कुछ ऐसा है जो एक जावा बराबर प्रतीत नहीं होता है के पार चला गया है: रेगुलर एक्सप्रेशन में हर मैच के लिएजावा PHP के preg_replace_callback

preg_replace_callback() 

, यह एक समारोह है कि एक पैरामीटर के रूप मैच पाठ पारित हो जाता है कहते हैं। उदाहरण के उपयोग के रूप में:

$articleText = preg_replace_callback("/\[thumb(\d+)\]/",'thumbReplace', $articleText); 
# ... 
function thumbReplace($matches) { 
    global $photos; 
    return "<img src=\"thumbs/" . $photos[$matches[1]] . "\">"; 
} 

जावा में ऐसा करने का आदर्श तरीका क्या होगा?

उत्तर

22

महत्वपूर्ण: जैसा कि टिप्पणी में Kip द्वारा कहे अनुसार, इस वर्ग करता है, तो मिलान regex प्रतिस्थापन स्ट्रिंग पर से मेल खाता है अनंत लूप बग है। यदि आवश्यक हो तो मैं इसे ठीक करने के लिए पाठकों को एक अभ्यास के रूप में छोड़ दूंगा।


मुझे जावा में निर्मित कुछ भी नहीं पता है। तुम भी अधिक कठिनाई के बिना अपने स्वयं के रोल कर सकता है, Matcher वर्ग का उपयोग कर:

import java.util.regex.*; 

public class CallbackMatcher 
{ 
    public static interface Callback 
    { 
     public String foundMatch(MatchResult matchResult); 
    } 

    private final Pattern pattern; 

    public CallbackMatcher(String regex) 
    { 
     this.pattern = Pattern.compile(regex); 
    } 

    public String replaceMatches(String string, Callback callback) 
    { 
     final Matcher matcher = this.pattern.matcher(string); 
     while(matcher.find()) 
     { 
      final MatchResult matchResult = matcher.toMatchResult(); 
      final String replacement = callback.foundMatch(matchResult); 
      string = string.substring(0, matchResult.start()) + 
        replacement + string.substring(matchResult.end()); 
      matcher.reset(string); 
     } 
    } 
} 

फिर फोन:

final CallbackMatcher.Callback callback = new CallbackMatcher.Callback() { 
    public String foundMatch(MatchResult matchResult) 
    { 
     return "<img src=\"thumbs/" + matchResults.group(1) + "\"/>"; 
    } 
}; 

final CallbackMatcher callbackMatcher = new CallbackMatcher("/\[thumb(\d+)\]/"); 
callbackMatcher.replaceMatches(articleText, callback); 

ध्यान दें कि आप matchResults.group() या matchResults.group(0) फोन करके पूरे मिलान किया स्ट्रिंग प्राप्त कर सकते हैं, तो यह नहीं है वर्तमान स्ट्रिंग स्थिति कॉलबैक पास करने के लिए आवश्यक है।

संपादित करें: इसे और अधिक पीएचपी समारोह का सही कार्यक्षमता की तरह लग रहे बनाया।

यहाँ मूल है, के बाद से प्रश्नकर्ता यह पसंद है:

public class CallbackMatcher 
{ 
    public static interface Callback 
    { 
     public void foundMatch(MatchResult matchResult); 
    } 

    private final Pattern pattern; 

    public CallbackMatcher(String regex) 
    { 
     this.pattern = Pattern.compile(regex); 
    } 

    public String findMatches(String string, Callback callback) 
    { 
     final Matcher matcher = this.pattern.matcher(string); 
     while(matcher.find()) 
     { 
      callback.foundMatch(matcher.toMatchResult()); 
     } 
    } 
} 

इस विशेष उपयोग के मामले के लिए, यह सबसे अच्छा होगा बस बाद में कॉलबैक में प्रत्येक मैच क़तार में है, तो पीछे की ओर उन के माध्यम से चला सकता है। यह इंडेक्स को रीमेप करने से रोक देगा क्योंकि स्ट्रिंग को संशोधित किया गया है।

+0

में दो कुत्तों मैं वास्तव में लौटे स्ट्रिंग और अनुक्रमित कतार के साथ बेहतर अपने मूल जवाब चाहते: ४६५१०४०३२१० वापसी मान का उत्पादन करेगा। फिर उन्हें विपरीत में लागू करना। इस तरह से सरल है, लेकिन लगता है कि प्रत्येक मैच के लिए पूरी स्ट्रिंग को पुन: स्कैन करने के लिए और अधिक काम करना प्रतीत होता है। सलाह के लिये धन्यवाद! – Mike

+0

मैंने मूल सुझाव को वापस जोड़ा। अपेक्षित इनपुट आकार इस बात को लेकर अंतर करेगा कि फिर से बदलना या कतार बदलना अधिक प्रभावी होगा या नहीं। मुझे लगता है कि प्रतिस्थापन स्ट्रिंग के साथ प्रतिस्थापन विधि कतार भी हो सकती है ... – jdmichal

+0

एरर ... मिस्पाक। स्पष्ट रूप से सीपीयू समय के संबंध में क्यूईइंग हमेशा अधिक प्रभावी होती है। अंतर यह होगा कि चिंता करने के लिए यह एक बड़ी समस्या है या नहीं। – jdmichal

-1

आपके सुझाव के साथ मैंने जो किया उसके अंतिम परिणाम यहां दिए गए हैं। मैंने सोचा कि किसी को भी एक ही समस्या होने पर यहां बाहर होना अच्छा लगेगा।

content = ReplaceCallback.find(content, regex, new ReplaceCallback.Callback() { 
    public String matches(MatchResult match) { 
     // Do something special not normally allowed in regex's... 
     return "newstring" 
    } 
}); 

पूरी क्लास सूची इस प्रकार है::

import java.util.regex.MatchResult; 
import java.util.regex.Pattern; 
import java.util.regex.Matcher; 
import java.util.Stack; 

/** 
* <p> 
* Class that provides a method for doing regular expression string replacement by passing the matched string to 
* a function that operates on the string. The result of the operation is then used to replace the original match. 
* </p> 
* <p>Example:</p> 
* <pre> 
* ReplaceCallback.find("string to search on", "/regular(expression/", new ReplaceCallback.Callback() { 
*  public String matches(MatchResult match) { 
*   // query db or whatever... 
*   return match.group().replaceAll("2nd level replacement", "blah blah"); 
*  } 
* }); 
* </pre> 
* <p> 
* This, in effect, allows for a second level of string regex processing. 
* </p> 
* 
*/ 
public class ReplaceCallback { 
    public static interface Callback { 
     public String matches(MatchResult match); 
    } 

    private final Pattern pattern; 
    private Callback callback; 

    private class Result { 
     int start; 
     int end; 
     String replace; 
    } 

    /** 
    * You probably don't need this. {@see find(String, String, Callback)} 
    * @param regex  The string regex to use 
    * @param callback An instance of Callback to execute on matches 
    */ 
    public ReplaceCallback(String regex, final Callback callback) { 
     this.pattern = Pattern.compile(regex); 
     this.callback = callback; 
    } 

    public String execute(String string) { 
     final Matcher matcher = this.pattern.matcher(string); 
     Stack<Result> results = new Stack<Result>(); 
     while(matcher.find()) { 
      final MatchResult matchResult = matcher.toMatchResult(); 
      Result r = new Result(); 
      r.replace = callback.matches(matchResult); 
      if(r.replace == null) 
       continue; 
      r.start = matchResult.start(); 
      r.end = matchResult.end(); 
      results.push(r); 
     } 
     // Improve this with a stringbuilder... 
     while(!results.empty()) { 
      Result r = results.pop(); 
      string = string.substring(0, r.start) + r.replace + string.substring(r.end); 
     } 
     return string; 
    } 

    /** 
    * If you wish to reuse the regex multiple times with different callbacks or search strings, you can create a 
    * ReplaceCallback directly and use this method to perform the search and replace. 
    * 
    * @param string The string we are searching through 
    * @param callback A callback instance that will be applied to the regex match results. 
    * @return The modified search string. 
    */ 
    public String execute(String string, final Callback callback) { 
     this.callback = callback; 
     return execute(string); 
    } 

    /** 
    * Use this static method to perform your regex search. 
    * @param search The string we are searching through 
    * @param regex  The regex to apply to the string 
    * @param callback A callback instance that will be applied to the regex match results. 
    * @return The modified search string. 
    */ 
    public static String find(String search, String regex, Callback callback) { 
     ReplaceCallback rc = new ReplaceCallback(regex, callback); 
     return rc.execute(search); 
    } 
} 
+0

मैं कॉलबैक को स्टोर करने के लिए एक आवृत्ति चर का उपयोग नहीं करता, बल्कि इसे पैरामीटर के रूप में पास करता हूं। इसे एक इंस्टेंस वैरिएबल के रूप में संग्रहीत करने से आपकी कक्षा में अनपेक्षित व्यवहार होता है जब एक ही समय में अलग-अलग धागे से बुलाया जाता है। (दूसरा कॉलबैक पहले और दूसरे से मैच प्राप्त करेगा)। – jdmichal

51

PHP के कॉलबैक सुविधा का अनुकरण करने की कोशिश कर रहा है काम का एक बहुत भयानक है जब आप सिर्फ appendReplacement() और appendTail (इस्तेमाल कर सकते हैं लगता है, जिसके परिणामस्वरूप बुला कोड की तरह दिखता है) एक पाश में:

StringBuffer resultString = new StringBuffer(); 
Pattern regex = Pattern.compile("regex"); 
Matcher regexMatcher = regex.matcher(subjectString); 
while (regexMatcher.find()) { 
    // You can vary the replacement text for each match on-the-fly 
    regexMatcher.appendReplacement(resultString, "replacement"); 
} 
regexMatcher.appendTail(resultString); 
+3

मुझे लगता है कि कुछ जेडीके कक्षाओं में शक्तिशाली विशेषताएं होती हैं लेकिन उन सुविधाओं को कभी-कभी अजीब वर्ग के नाम या अजीब विधि नामों के पीछे छिपाया जाता है ... हालांकि 'ऐपेंड रिप्लेसमेंट/एपेंडटेल' रणनीति, जैसा कि यहां उपयोग किया गया है, को कम कोड की आवश्यकता है, 'कॉलबैक' रणनीति (ओपी का चुना गया जवाब) स्पष्ट, अधिक स्पष्ट है! – Stephan

+0

यदि सही प्रतिस्थापन प्राप्त करने के लिए मुझे स्ट्रिंग मिलान करने की आवश्यकता है तो क्या होगा? कहें कि विषय स्ट्रिंग में "फू बार" हो सकता है लेकिन मुझे "गोवार्ट्स" द्वारा "जन" और "बार" द्वारा "foo" को प्रतिस्थापित करने की आवश्यकता है? – ALOToverflow

+0

लूप के अंदर 'foo | bar' का उपयोग अपने regex और क्वेरी' regexMatcher.group() 'के रूप में करें ताकि यह देखने के लिए कि आपको किस प्रतिस्थापन को जोड़ने की आवश्यकता है। –

0

मैंने पाया कि jdmichal के जवाब पाश अनंत यदि आपका लौटे स्ट्रिंग फिर से मिलान किया जा सकता है; नीचे एक संशोधन है जो इस मिलान से अनंत लूप को रोकता है।

public String replaceMatches(String string, Callback callback) { 
    String result = ""; 
    final Matcher matcher = this.pattern.matcher(string); 
    int lastMatch = 0; 
    while(matcher.find()) 
    { 
     final MatchResult matchResult = matcher.toMatchResult(); 
     final String replacement = callback.foundMatch(matchResult); 
     result += string.substring(lastMatch, matchResult.start()) + 
      replacement; 
     lastMatch = matchResult.end(); 
    } 
    if (lastMatch < string.length()) 
     result += string.substring(lastMatch); 
    return result; 
} 
3

मैं यहां किसी भी समाधान से काफी संतुष्ट नहीं था। मैं एक स्टेटलेस समाधान चाहता था। और अगर मैं प्रतिस्थापन स्ट्रिंग पैटर्न से मेल खाने के लिए हुआ तो मैं एक अनंत लूप में समाप्त नहीं होना चाहता था। जबकि मैं उस पर था, मैंने limit पैरामीटर के लिए समर्थन जोड़ा और count पैरामीटर लौटा दिया।(मैंने संदर्भ द्वारा एक पूर्णांक गुजरने के अनुकरण के लिए AtomicInteger का उपयोग किया था।) मैंने अज्ञात वर्ग को परिभाषित करना आसान बनाने के लिए, पैरामीटर सूची के अंत में callback पैरामीटर को स्थानांतरित किया।

final Map<String,String> props = new HashMap<String,String>(); 
props.put("MY_NAME", "Kip"); 
props.put("DEPT", "R&D"); 
props.put("BOSS", "Dave"); 

String subjectString = "Hi my name is ${MY_NAME} and I work in ${DEPT} for ${BOSS}"; 
String sRegex = "\\$\\{([A-Za-z0-9_]+)\\}"; 

String replacement = ReplaceCallback.replace(sRegex, subjectString, new ReplaceCallback.Callback() { 
    public String matchFound(MatchResult match) { 
    String group1 = match.group(1); 
    if(group1 != null && props.containsKey(group1)) 
     return props.get(group1); 
    return match.group(); 
    } 
}); 

System.out.println("replacement: " + replacement); 

यहाँ और ReplaceCallback वर्ग की मेरी संस्करण है:

import java.util.concurrent.atomic.AtomicInteger; 
import java.util.regex.*; 

public class ReplaceCallback 
{ 
    public static interface Callback { 
    /** 
    * This function is called when a match is made. The string which was matched 
    * can be obtained via match.group(), and the individual groupings via 
    * match.group(n). 
    */ 
    public String matchFound(MatchResult match); 
    } 

    /** 
    * Replaces with callback, with no limit to the number of replacements. 
    * Probably what you want most of the time. 
    */ 
    public static String replace(String pattern, String subject, Callback callback) 
    { 
    return replace(pattern, subject, -1, null, callback); 
    } 

    public static String replace(String pattern, String subject, int limit, Callback callback) 
    { 
    return replace(pattern, subject, limit, null, callback); 
    } 

    /** 
    * @param regex The regular expression pattern to search on. 
    * @param subject The string to be replaced. 
    * @param limit The maximum number of replacements to make. A negative value 
    *     indicates replace all. 
    * @param count If this is not null, it will be set to the number of 
    *     replacements made. 
    * @param callback Callback function 
    */ 
    public static String replace(String regex, String subject, int limit, 
      AtomicInteger count, Callback callback) 
    { 
    StringBuffer sb = new StringBuffer(); 
    Matcher matcher = Pattern.compile(regex).matcher(subject); 
    int i; 
    for(i = 0; (limit < 0 || i < limit) && matcher.find(); i++) 
    { 
     String replacement = callback.matchFound(matcher.toMatchResult()); 
     replacement = Matcher.quoteReplacement(replacement); //probably what you want... 
     matcher.appendReplacement(sb, replacement); 
    } 
    matcher.appendTail(sb); 

    if(count != null) 
     count.set(i); 
    return sb.toString(); 
    } 
} 
0
public static String replace(Pattern pattern, Function<MatchResult, String> callback, CharSequence subject) { 
    Matcher m = pattern.matcher(subject); 
    StringBuffer sb = new StringBuffer(); 
    while (m.find()) { 
     m.appendReplacement(sb, callback.apply(m.toMatchResult())); 
    } 
    m.appendTail(sb); 
    return sb.toString(); 
} 

प्रयोग उदाहरण:

replace(Pattern.compile("cat"), mr -> "dog", "one cat two cats in the yard") 

यहाँ उपयोग का एक उदाहरण है

एक कुत्ता यार्ड

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