2009-07-16 4 views
9

बिना एक स्ट्रिंग में टोकन को बदलने के लिए एक स्ट्रिंग इसलिए की तरह देखते हुए:कैसे StringTokenizer

Hello {FIRST_NAME}, this is a personalized message for you. 

कहाँ FIRST_NAME एक मनमाना टोकन (एक नक्शे के विधि को पास किए जाने में एक महत्वपूर्ण), एक नियमित जो चालू करेंगे लिखना है उस स्ट्रिंग में:

Hello Jim, this is a personalized message for you. 

प्रविष्टि के साथ एक मानचित्र FIRST_NAME -> जिम दिया गया।

ऐसा प्रतीत होता है कि स्ट्रिंगटोकनाइज़र सबसे सीधे आगे दृष्टिकोण है, लेकिन जावाडॉक्स वास्तव में कहता है कि आपको रेगेक्स अप्राच का उपयोग करना पसंद करना चाहिए। रेगेक्स आधारित समाधान में आप इसे कैसे करेंगे?

+0

http://github.com/niesfisch/tokenreplacer/ – Marcel

उत्तर

4

इस प्रयास करें:

नोट:author's final solution इस नमूने पर बनाता है और भी बहुत कुछ संक्षिप्त है।

public class TokenReplacer { 

    private Pattern tokenPattern; 

    public TokenReplacer() { 
     tokenPattern = Pattern.compile("\\{([^}]+)\\}"); 
    } 

    public String replaceTokens(String text, Map<String, String> valuesByKey) { 
     StringBuilder output = new StringBuilder(); 
     Matcher tokenMatcher = tokenPattern.matcher(text); 

     int cursor = 0; 
     while (tokenMatcher.find()) { 
      // A token is defined as a sequence of the format "{...}". 
      // A key is defined as the content between the brackets. 
      int tokenStart = tokenMatcher.start(); 
      int tokenEnd = tokenMatcher.end(); 
      int keyStart = tokenMatcher.start(1); 
      int keyEnd = tokenMatcher.end(1); 

      output.append(text.substring(cursor, tokenStart)); 

      String token = text.substring(tokenStart, tokenEnd); 
      String key = text.substring(keyStart, keyEnd); 

      if (valuesByKey.containsKey(key)) { 
       String value = valuesByKey.get(key); 
       output.append(value); 
      } else { 
       output.append(token); 
      } 

      cursor = tokenEnd; 
     } 
     output.append(text.substring(cursor)); 

     return output.toString(); 
    } 

} 
+0

वह प्रत्येक पंक्ति के लिए पैटर्न को पुन: संकलित करेगा। मैं जितना संभव हो उतना संकलित के रूप में अपने पैटर्न पसंद करते हैं! :-) इसके अलावा, आप बेहतर टोकन के अस्तित्व की जांच करेंगे। –

+0

मेरा मतलब है, मानचित्र में टोकनिक्सिस्ट की जांच करें। –

+0

आप हर बार संकलित करने से बचने के लिए इस विधि को जो भी कक्षा में शामिल करेंगे, उसका एक उदाहरण चर 'टोकनपेटर्न' बना सकता है। कोड स्वचालित रूप से उस स्थिति को समायोजित करेगा जिससे कोई टोकन नहीं पता चला है ('output.append (text.substring (कर्सर))')। –

0

दस्तावेज़ों का मतलब है कि आपको रेगेक्स-आधारित टोकनेज़र, आईआईआरसी लिखना पसंद करना चाहिए। आपके लिए बेहतर काम क्या हो सकता है मानक रेगेक्स खोज-प्रतिस्थापन।

6
String.replaceAll("{FIRST_NAME}", actualName); 

इसके लिए javadocs देखें here

+0

का प्रदर्शन ओ (एन * के) होगा, जहां एन इनपुट स्ट्रिंग का आकार है, और के कुंजी की संख्या है। –

+0

@Daniel क्या आपने उस निष्कर्ष पर आने के लिए स्रोत कोड पढ़ा था? जावा तारों के साथ कुछ सुंदर बुद्धिमान चीजें करता है। मैं उम्मीद करता हूं कि एक बहुत अच्छा मौका है जिससे आप किसी अन्य समाधान को बेहतर प्रदर्शन कर सकेंगे। –

+0

@ बिलक मुझे लगता है कि उसका मतलब हो सकता है कि यदि आपको स्ट्रिंग में प्रतिस्थापित करने के लिए एक से अधिक कुंजी हैं, तो '* k'' आपको बार-बार 'replaceAll'' कॉल करना होगा। – Svish

8

ठीक है, मैं स्ट्रिंग.फॉर्मैट(), या बेहतर MessageFormat का उपयोग करना चाहूंगा।

2

सबसे सीधे आगे इस की तर्ज पर कुछ होने लगते हैं:

public static void main(String[] args) { 
    String tokenString = "Hello {FIRST_NAME}, this is a personalized message for you."; 
    Map<String, String> tokenMap = new HashMap<String, String>(); 
    tokenMap.put("{FIRST_NAME}", "Jim"); 
    String transformedString = tokenString; 
    for (String token : tokenMap.keySet()) { 
     transformedString = transformedString.replace(token, tokenMap.get(token)); 
    } 
    System.out.println("New String: " + transformedString); 
} 

यह आपके सभी टोकन के माध्यम से लूप और आपको क्या चाहिए के साथ हर टोकन ले लेती है और मानक स्ट्रिंग विधि का उपयोग करता प्रतिस्थापन के लिए, इस प्रकार पूरे RegEx निराशा छोड़ना।

+2

इसका मतलब प्रत्येक टोकन के लिए पूरी स्ट्रिंग को पढ़ना होगा। यदि आपके पास प्रक्रिया के लिए के टोकन और एन बाइट हैं, तो एल्गोरिदम में ऑर्डर ओ (एन * के) होगा। बहुत अक्षम –

+1

सैद्धांतिक रूप से, यह ओ (एन * के) जैसा कहा गया है, लेकिन आपका कथन मेरे लिए समयपूर्व अनुकूलन जैसा लगता है। इस एल्गोरिदम को कितनी बार बुलाया जाता है, इस बारे में और जानने के बिना, स्ट्रिंग में कितने टोकन मौजूद हैं, स्ट्रिंग कितनी देर तक है, और कितना महत्वपूर्ण समय बचा रहा है, यह कहना असंभव है कि अक्षमता पर असर कितना बड़ा है। यदि इसे केवल 10 एमएस के कुल रन टाइम के साथ ही बुलाया जाता है, भले ही यह 1 एमएस (उदाहरण के लिए) पर कुशल हो सकता है, निश्चित रूप से यह इसकी तुलना में धीमी गति का एक आदेश हो सकता है, लेकिन प्रदर्शन दंड वास्तव में पर्याप्त है चीजों की भव्य योजना में? – Peter

3
आयात java.util.regex साथ

*:।

Pattern p = Pattern.compile("{([^{}]*)}"); 
Matcher m = p.matcher(line); // line being "Hello, {FIRST_NAME}..." 
while (m.find) { 
    String key = m.group(1); 
    if (map.containsKey(key)) { 
    String value= map.get(key); 
    m.replaceFirst(value); 
    } 
} 

तो, regex की सिफारिश की है क्योंकि यह आसानी से स्थानों है कि स्ट्रिंग में प्रतिस्थापन की आवश्यकता होती है पहचान कर सकते हैं, साथ ही कुंजी का नाम निकालने प्रतिस्थापन के लिए। यह पूरी स्ट्रिंग तोड़ने से कहीं अधिक कुशल है।

आप शायद मैचर लाइन के अंदर और पैटर्न लाइन के बाहर लूप करना चाहते हैं, ताकि आप सभी लाइनों को प्रतिस्थापित कर सकें। पैटर्न को फिर से तैयार करने की आवश्यकता नहीं है, और यह अनावश्यक रूप से करने से बचने के लिए और अधिक कुशल है।

+1

m.group (0) पूरा मिलान है (यानी {FIRST_NAME})। m.group (1) केवल कुंजी होगी (यानी FIRST_NAME)। –

+0

पकड़ –

2

आपकी स्ट्रिंग को हास्यास्पद रूप से जटिल करने के आधार पर, आप Velocity की तरह एक और गंभीर स्ट्रिंग टेम्पलेटिंग भाषा का उपयोग करने का प्रयास कर सकते हैं। वेग के मामले में, आप कुछ इस तरह करते हैं:

Velocity.init(); 
VelocityContext context = new VelocityContext(); 
context.put("name", "Bob"); 
StringWriter output = new StringWriter(); 
Velocity.evaluate(context, output, "", 
     "Hello, #name, this is a personalized message for you."); 
System.out.println(output.toString()); 

लेकिन संभावना है कि overkill है अगर आप केवल एक या दो मूल्यों को बदलने के लिए चाहते हैं।

1
import java.util.HashMap; 

public class ReplaceTest { 

    public static void main(String[] args) { 
    HashMap<String, String> map = new HashMap<String, String>(); 

    map.put("FIRST_NAME", "Jim"); 
    map.put("LAST_NAME", "Johnson"); 
    map.put("PHONE",  "410-555-1212"); 

    String s = "Hello {FIRST_NAME} {LAST_NAME}, this is a personalized message for you."; 

    for (String key : map.keySet()) { 
     s = s.replaceAll("\\{" + key + "\\}", map.get(key)); 
    } 

    System.out.println(s); 
    } 

} 
11

उत्तर के लिए सभी को धन्यवाद!

Gizmo का उत्तर निश्चित रूप से बॉक्स से बाहर था, और एक अच्छा समाधान था, लेकिन दुर्भाग्य से उचित नहीं है क्योंकि प्रारूप इस मामले में फ़ॉर्मेटर वर्ग के लिए सीमित नहीं हो सकता है।

एडम पेन्टर वास्तव में सही पैटर्न के साथ इस मामले के दिल में पहुंचे।

पीटर निक्स और शॉन ब्राइट ने रेगेक्स की सभी जटिलताओं से बचने के लिए एक अच्छा कामकाज किया था, लेकिन अगर खराब टोकन थे तो मुझे कुछ त्रुटियां उठाने की ज़रूरत थी, जो ऐसा नहीं करते थे।

लेकिन दोनों रेगेक्स और उचित प्रतिस्थापन लूप करने के मामले में, यह वह उत्तर है जिसके साथ मैं आया था (Google और थोड़ी सी मदद के साथ, समूह का उपयोग कैसे करें (1) के बारे में शॉन ब्राइट की टिप्पणी सहित बनाम समूह()):

private static Pattern tokenPattern = Pattern.compile("\\{([^}]*)\\}"); 

public static String process(String template, Map<String, Object> params) { 
    StringBuffer sb = new StringBuffer(); 
    Matcher myMatcher = tokenPattern.matcher(template); 
    while (myMatcher.find()) { 
     String field = myMatcher.group(1); 
     myMatcher.appendReplacement(sb, ""); 
     sb.append(doParameter(field, params)); 
    } 
    myMatcher.appendTail(sb); 
    return sb.toString(); 
} 

कहाँ doParameter नक्शे से बाहर निकलने मूल्य हो जाता है और एक स्ट्रिंग में बदल देता है और एक अपवाद फेंकता है तो यह वहाँ नहीं है।

नोट भी मैंने खाली ब्रेसिज़ (यानी {}) खोजने के लिए पैटर्न बदल दिया, क्योंकि यह एक त्रुटि स्थिति स्पष्ट रूप से जांच की गई है।

संपादित करें: ध्यान दें कि संलग्न करना स्ट्रिंग की सामग्री के बारे में अज्ञात नहीं है। Javadocs के अनुसार, यह एक विशेष चरित्र के रूप में $ और बैकस्लैश को पहचानता है, इसलिए मैंने उपरोक्त नमूने को संभालने के लिए कुछ भागने को जोड़ा। सबसे प्रदर्शन जागरूक तरीके से नहीं किया गया है, लेकिन मेरे मामले में यह स्ट्रिंग क्रिएशन को माइक्रो-ऑप्टिमाइज़ करने का प्रयास करने के लायक होने का एक बड़ा सौदा नहीं है।

एलन एम से टिप्पणी के लिए धन्यवाद, इसे एपेंड रिप्लेसमेंट के विशेष चरित्र मुद्दों से बचने के लिए और भी आसान बनाया जा सकता है।

+0

के लिए धन्यवाद यह एक बहुत अच्छा जवाब है। यह एक शर्म की बात है कि मैंने JavaDocs को पूरी तरह से नहीं पढ़ा ... –

+1

आपको प्रतिस्थापन से बचने की आवश्यकता नहीं है, बस इसे एपेंड से दूर रखें रिप्लेसमेंट(): 'myMatcher.appendReplacement (sb, ""); sb.append (doParameter (फ़ील्ड, पैराम्स)); ' –

+0

इस अद्यतन को शामिल करने के लिए धन्यवाद बहुत उपयोगी प्रश्न और उत्तर! –

0

आम तौर पर हम इस तरह के मामले में संदेशफॉर्मैट का उपयोग करेंगे, साथ ही संसाधन बंडल से वास्तविक संदेश टेक्स्ट लोड करने के साथ-साथ। यह आपको G10N अनुकूल होने का अतिरिक्त लाभ देता है।

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