स्ट्रिंग स्वरूपण में नामांकित प्लेसहोल्डर


175

पायथन में, जब स्ट्रिंग को प्रारूपित किया जाता है, तो मैं स्थिति के बजाय नाम से प्लेसहोल्डर्स को भर सकता हूं, जैसे:

print "There's an incorrect value '%(value)s' in column # %(column)d" % \
  { 'value': x, 'column': y }

मुझे आश्चर्य है कि अगर जावा में यह संभव है (उम्मीद है, बाहरी पुस्तकालयों के बिना)?


आप MessageFormat का विस्तार कर सकते हैं और वैरिएबल से लेकर सूचकांकों तक मैपिंग की फ़ंक्शनलिटी लागू कर सकते हैं।
vpram86


1
कुछ इतिहास: जावा ने इस मामले पर ज्यादातर C / C ++ की नकल की क्योंकि इसने C ++ दुनिया के डेवलपर्स को लुभाने की कोशिश की %sथी जहां यह आम बात थी। en.wikipedia.org/wiki/Printf_format_string#History यह भी ध्यान दें कि कुछ आईडीई और फाइंडबग्स स्वचालित रूप से% s और% d मायने रखता है बेमेल का पता लगा सकते हैं, लेकिन मैं अभी भी नामित क्षेत्रों को पसंद करूंगा।
क्रिस्टोफ रूसो

जवाबों:


143

जकार्ता कॉमन्स लैंग का स्ट्रैसब्यूटिसेटर ऐसा करने का एक हल्का वजन तरीका है बशर्ते आपके मूल्य पहले से ही सही ढंग से स्वरूपित हों।

http://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/text/StrSubstitutor.html

Map<String, String> values = new HashMap<String, String>();
values.put("value", x);
values.put("column", y);
StrSubstitutor sub = new StrSubstitutor(values, "%(", ")");
String result = sub.replace("There's an incorrect value '%(value)' in column # %(column)");

उपरोक्त परिणाम:

"कॉलम # 2 में एक गलत मान '1' है"

मावेन का उपयोग करते समय आप इस निर्भरता को अपने pom.xml में जोड़ सकते हैं:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>

2
मैंने यह निराशाजनक पाया कि यदि कुंजी नहीं मिली, तो पुस्तकालय फेंक नहीं देता है, हालाँकि, यदि आप ${arg}ऊपर दिए गए रिवाज ( ) के बजाय डिफ़ॉल्ट सिंटैक्स ( ) का उपयोग करते हैं, तो %(arg)रेगेक्स संकलन नहीं करेगा, जो वांछित प्रभाव है।
जॉन लेहमैन

2
यदि आप मानचित्र में मौजूद नहीं है, तो आप एक कस्टम VariableResolver सेट कर सकते हैं और एक अपवाद फेंक सकते हैं।
मेने

7
पुराना धागा, लेकिन 3.6 के रूप में, पाठ पैकेज को कॉमन्स-टेक्स्ट के पक्ष में चित्रित किया गया था। commons.apache.org/proper/commons-text
जेफ वॉकर

74

काफी नहीं, लेकिन आप कई बार एक मान को संदर्भित करने के लिए MessageFormat का उपयोग कर सकते हैं :

MessageFormat.format("There's an incorrect value \"{0}\" in column # {1}", x, y);

उपरोक्त String.format () के साथ भी किया जा सकता है, लेकिन मुझे संदेश मिल जाता है यदि आप जटिल अभिव्यक्तियों का निर्माण करने की आवश्यकता करते हैं तो सिरामैक्स क्लीनर, प्लस आपको स्ट्रिंग में डाल रहे ऑब्जेक्ट के प्रकार के बारे में परवाह करने की आवश्यकता नहीं है।


यह निश्चित नहीं है कि आप क्यों नहीं कर सकते हैं, स्ट्रिंग में स्थिति महत्वपूर्ण नहीं है, केवल आर्ग की सूची में स्थिति है, जो इसे नाम बदलने की समस्या बनाती है। आप कुंजियों का नाम जानते हैं, जिसका अर्थ है कि आप तर्कों की सूची में एक कुंजी के लिए स्थिति तय कर सकते हैं। अब से मान 0 के रूप में और स्तंभ 1 के रूप में जाना जाएगा: MessageeFormat.format ("कॉलम # {1} में एक गलत मान \" {0} \ "है, {0} का उपयोग करके मान कई समस्याओं का कारण बन सकता है", valueMap .get ('मूल्य'), valueMap.get ('स्तंभ'));
गिलादबू

1
एक सुराग के लिए धन्यवाद, इसने मुझे सरल कार्य लिखने में मदद की जो कि मैं चाहता हूं (मैंने इसे नीचे रखा है)।
एंडी

1
सहमत, वाक्यविन्यास बहुत क्लीनर है। बहुत बुरा MessageFormat अपने स्वयं के दिमाग मिल गया है जब यह संख्यात्मक मूल्यों को स्वरूपित करने की बात आती है।
कीस डी कुटर

और यह एकल उद्धरणों से घिरे प्लेसहोल्डर्स को नजरअंदाज करने के लिए लगता है।
कीस डी कूटर

MessageFormatअपेक्षाकृत बड़े
जोंस

32

सरल नाम के प्लेसहोल्डर के लिए अपाचे कॉमन स्ट्रिंगरुस्टिष्ट का एक और उदाहरण ।

String template = "Welcome to {theWorld}. My name is {myName}.";

Map<String, String> values = new HashMap<>();
values.put("theWorld", "Stackoverflow");
values.put("myName", "Thanos");

String message = StringSubstitutor.replace(template, values, "{", "}");

System.out.println(message);

// Welcome to Stackoverflow. My name is Thanos.

यदि आप बहुत बड़ी फ़ाइलों को लोड करने की उम्मीद करते हैं, तो मैंने पाया कि यह लाइब्रेरी भी replaceInएक बफर में कौन से विकल्प मानों का समर्थन करती है: स्ट्रिंगबर्स्ट, या टेक्स्टस्ट्रिंगबर्स्ट। इस दृष्टिकोण के साथ, फ़ाइल की संपूर्ण सामग्री को मेमोरी में लोड नहीं किया जाएगा।
एडवर्ड कोरिगैल

15

आप StringTemplate लाइब्रेरी का उपयोग कर सकते हैं , यह वही प्रदान करता है जो आप चाहते हैं और बहुत कुछ।

import org.antlr.stringtemplate.*;

final StringTemplate hello = new StringTemplate("Hello, $name$");
hello.setAttribute("name", "World");
System.out.println(hello.toString());

'चार के साथ परेशानी थी :unexpected char: '''
AlikElzin-kilaka

11

के लिए बहुत ही सरल मामलों आप बस वहाँ एक पुस्तकालय के लिए कोई ज़रूरत नहीं एक hardcoded स्ट्रिंग की जगह उपयोग कर सकते हैं,:

    String url = "There's an incorrect value '%(value)' in column # %(column)";
    url = url.replace("%(value)", x); // 1
    url = url.replace("%(column)", y); // 2

चेतावनी : मैं केवल सबसे सरल कोड दिखाना चाहता था। बेशक गंभीर उत्पादन कोड के लिए इसका उपयोग न करें जहां सुरक्षा मामले, जैसा कि टिप्पणियों में कहा गया है: भागने, त्रुटि से निपटने और सुरक्षा यहां एक मुद्दा है। लेकिन सबसे बुरे मामले में अब आप जानते हैं कि 'अच्छे' परिवाद का उपयोग करने की आवश्यकता क्यों है :-)


1
यह सरल और आसान है, लेकिन नकारात्मक पक्ष यह है कि जब मूल्य नहीं मिला, तो यह चुपचाप विफल रहता है। यह सिर्फ मूल स्ट्रिंग में प्लेसहोल्डर छोड़ देता है।
kiedysktos

@kiedysktos, आप चेक करके उस पर सुधार कर सकते हैं, लेकिन अगर आप पूरी चीज़ चाहते हैं, तो एक काम का उपयोग करें :)
Christophe Roussy

2
चेतावनी: क्योंकि यह तकनीक मध्यवर्ती प्रतिस्थापन परिणामों को उनके स्वयं के प्रारूप के तार के रूप में मानती है, यह समाधान स्ट्रिंग हमलों को प्रारूपित करने के लिए असुरक्षित है । किसी भी सही समाधान को प्रारूप स्ट्रिंग के माध्यम से एक एकल पास बनाना चाहिए।
200_सफल

@ 200_success हाँ सुरक्षा के बारे में बात करना अच्छी बात है, बेशक यह कोड गंभीर उत्पादन उपयोग के लिए नहीं है ...
क्रिस्टोफ़ रूसो

8

आपकी सभी मदद का धन्यवाद! आपके सभी सुरागों का उपयोग करते हुए, मैंने नियमित रूप से वही किया है जो मैं चाहता हूं - शब्दकोश का उपयोग करते हुए अजगर जैसा स्ट्रिंग स्वरूपण। चूंकि मैं जावा नौसिखिया हूं, किसी भी संकेत की सराहना की जाती है।

public static String dictFormat(String format, Hashtable<String, Object> values) {
    StringBuilder convFormat = new StringBuilder(format);
    Enumeration<String> keys = values.keys();
    ArrayList valueList = new ArrayList();
    int currentPos = 1;
    while (keys.hasMoreElements()) {
        String key = keys.nextElement(),
        formatKey = "%(" + key + ")",
        formatPos = "%" + Integer.toString(currentPos) + "$";
        int index = -1;
        while ((index = convFormat.indexOf(formatKey, index)) != -1) {
            convFormat.replace(index, index + formatKey.length(), formatPos);
            index += formatPos.length();
        }
        valueList.add(values.get(key));
        ++currentPos;
    }
    return String.format(convFormat.toString(), valueList.toArray());
}

लोमबो के जवाब के विपरीत, यह एक अनंत लूप में फंस नहीं सकता है, क्योंकि formatPosइसमें नहीं हो सकता है formatKey
एरोन डुफोर

6
चेतावनी: क्योंकि लूप मध्यवर्ती प्रतिस्थापन परिणामों को अपने स्वयं के स्वरूपण के रूप में व्यवहार करता है, यह समाधान स्ट्रिंग हमलों को स्वरूपित करने के लिए असुरक्षित है । किसी भी सही समाधान को प्रारूप स्ट्रिंग के माध्यम से एक एकल पास बनाना चाहिए।
200_सफल

6

यह एक पुराना धागा है, लेकिन सिर्फ रिकॉर्ड के लिए, आप इस तरह जावा 8 शैली का उपयोग कर सकते हैं:

public static String replaceParams(Map<String, String> hashMap, String template) {
    return hashMap.entrySet().stream().reduce(template, (s, e) -> s.replace("%(" + e.getKey() + ")", e.getValue()),
            (s, s2) -> s);
}

उपयोग:

public static void main(String[] args) {
    final HashMap<String, String> hashMap = new HashMap<String, String>() {
        {
            put("foo", "foo1");
            put("bar", "bar1");
            put("car", "BMW");
            put("truck", "MAN");
        }
    };
    String res = replaceParams(hashMap, "This is '%(foo)' and '%(foo)', but also '%(bar)' '%(bar)' indeed.");
    System.out.println(res);
    System.out.println(replaceParams(hashMap, "This is '%(car)' and '%(foo)', but also '%(bar)' '%(bar)' indeed."));
    System.out.println(replaceParams(hashMap, "This is '%(car)' and '%(truck)', but also '%(foo)' '%(bar)' + '%(truck)' indeed."));
}

उत्पादन होगा:

This is 'foo1' and 'foo1', but also 'bar1' 'bar1' indeed.
This is 'BMW' and 'foo1', but also 'bar1' 'bar1' indeed.
This is 'BMW' and 'MAN', but also 'foo1' 'bar1' + 'MAN' indeed.

यह शानदार है, लेकिन दुख की बात है कि यह चश्मा का उल्लंघन करता है यहां docs.oracle.com/javase/8/docs/api/java/util/stream/… कंबाइन फ़ंक्शन को दूसरा पैरामीटर वापस करना होगा यदि पहला पैराम की पहचान है। ऊपर वाला इसके बजाय पहचान लौटाएगा। यह इस नियम का भी उल्लंघन करता है: combiner.apply (u, संचायक
.apply

दिलचस्प है ... लेकिन केवल यदि आप नक्शे को पास करने के लिए एक अच्छे तरीके का प्रस्ताव करते हैं, तो भी यदि संभव हो तो सबसे प्रारूपण कोड जैसे टेम्पलेट के बाद।
क्रिस्टोफ रूसो

4
चेतावनी: क्योंकि .reduce()इंटरमीडिएट प्रतिस्थापन प्रतिस्थापन का परिणाम स्वयं के प्रारूप के तार के रूप में होता है, यह समाधान स्ट्रिंग हमलों को प्रारूपित करने के लिए असुरक्षित है । किसी भी सही समाधान को प्रारूप स्ट्रिंग के माध्यम से एक एकल पास बनाना चाहिए।
200_सफल

6
public static String format(String format, Map<String, Object> values) {
    StringBuilder formatter = new StringBuilder(format);
    List<Object> valueList = new ArrayList<Object>();

    Matcher matcher = Pattern.compile("\\$\\{(\\w+)}").matcher(format);

    while (matcher.find()) {
        String key = matcher.group(1);

        String formatKey = String.format("${%s}", key);
        int index = formatter.indexOf(formatKey);

        if (index != -1) {
            formatter.replace(index, index + formatKey.length(), "%s");
            valueList.add(values.get(key));
        }
    }

    return String.format(formatter.toString(), valueList.toArray());
}

उदाहरण:

String format = "My name is ${1}. ${0} ${1}.";

Map<String, Object> values = new HashMap<String, Object>();
values.put("0", "James");
values.put("1", "Bond");

System.out.println(format(format, values)); // My name is Bond. James Bond.

2
यह उत्तर होना चाहिए, क्योंकि यह प्रारूप स्ट्रिंग हमलों से बचता है जो यहां अन्य समाधानों में से अधिकांश के लिए असुरक्षित हैं। ध्यान दें कि .replaceAll()स्ट्रिंग प्रतिस्थापन कॉलबैक के लिए समर्थन के साथ Java 9 इसे बहुत सरल बनाता है ।
200_सफल

यह उत्तर होना चाहिए, इसके लिए यह किसी भी बाहरी पुस्तकालयों का उपयोग नहीं करता है।
बोहो एलआई

3

मैं एक छोटे से पुस्तकालय का लेखक हूं जो वास्तव में वही करता है जो आप चाहते हैं:

Student student = new Student("Andrei", 30, "Male");

String studStr = template("#{id}\tName: #{st.getName}, Age: #{st.getAge}, Gender: #{st.getGender}")
                    .arg("id", 10)
                    .arg("st", student)
                    .format();
System.out.println(studStr);

या आप तर्कों को श्रृंखलाबद्ध कर सकते हैं:

String result = template("#{x} + #{y} = #{z}")
                    .args("x", 5, "y", 10, "z", 15)
                    .format();
System.out.println(result);

// Output: "5 + 10 = 15"

क्या आपके पुस्तकालय के साथ हालत आधारित प्रारूपण करना संभव है?
गौरव

@ गौरव काफी नहीं। यदि आपको जरूरत है कि आपको एक पूर्ण-विशेषताओं वाली टेम्प्लेटिंग लाइब्रेरी की आवश्यकता है।
आंद्रेई सिओबानु

2

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

StringUtils.replaceEach("There's an incorrect value '%(value)' in column # %(column)",
            new String[] { "%(value)", "%(column)" }, new String[] { x, y });

कुछ इनपुट पाठ को देखते हुए, यह पहले स्ट्रिंग सरणी में प्लेसहोल्डर्स के सभी आवृत्तियों को दूसरे में संबंधित मानों के साथ बदल देगा।


1

आप एक स्ट्रिंग हेल्पर क्लास पर ऐसा कुछ कर सकते हैं

/**
 * An interpreter for strings with named placeholders.
 *
 * For example given the string "hello %(myName)" and the map <code>
 *      <p>Map<String, Object> map = new HashMap<String, Object>();</p>
 *      <p>map.put("myName", "world");</p>
 * </code>
 *
 * the call {@code format("hello %(myName)", map)} returns "hello world"
 *
 * It replaces every occurrence of a named placeholder with its given value
 * in the map. If there is a named place holder which is not found in the
 * map then the string will retain that placeholder. Likewise, if there is
 * an entry in the map that does not have its respective placeholder, it is
 * ignored.
 *
 * @param str
 *            string to format
 * @param values
 *            to replace
 * @return formatted string
 */
public static String format(String str, Map<String, Object> values) {

    StringBuilder builder = new StringBuilder(str);

    for (Entry<String, Object> entry : values.entrySet()) {

        int start;
        String pattern = "%(" + entry.getKey() + ")";
        String value = entry.getValue().toString();

        // Replace every occurence of %(key) with value
        while ((start = builder.indexOf(pattern)) != -1) {
            builder.replace(start, start + pattern.length(), value);
        }
    }

    return builder.toString();
}

बहुत बहुत धन्यवाद, यह लगभग वही करता है जो मैं चाहता हूं, लेकिन केवल एक चीज यह है कि इसमें संशोधक नहीं हैं ("% (कुंजी) 08 डी" पर विचार करें)
एंडी

1
ध्यान दें कि यह एक अनंत लूप में जाता है यदि उपयोग किए जा रहे किसी भी मान में संबंधित प्रविष्टि होती है।
एरोन डुफोर

1
चेतावनी: क्योंकि लूप मध्यवर्ती प्रतिस्थापन परिणामों को अपने स्वयं के स्वरूपण के रूप में व्यवहार करता है, यह समाधान स्ट्रिंग हमलों को स्वरूपित करने के लिए असुरक्षित है । किसी भी सही समाधान को प्रारूप स्ट्रिंग के माध्यम से एक एकल पास बनाना चाहिए।
200_सेकंड

1

मेरा जवाब है:

a) जब संभव हो तो StringBuilder का उपयोग करें

ख) किसी भी रूप में रखना (पूर्णांक: डॉलर मैक्रो की तरह सबसे अच्छा, स्पिरिचुअल चार) "प्लेसहोल्डर" की स्थिति और फिर उपयोग StringBuilder.insert()(कुछ संस्करणों के तर्क)।

बाहरी पुस्तकालयों का उपयोग ओवरकिल लगता है और मैं नीचा प्रदर्शन को महत्वपूर्ण मानता हूं, जब StringBuilder आंतरिक रूप से स्ट्रिंग में परिवर्तित हो जाता है।


1

उत्तर के आधार पर मैंने MapBuilderकक्षा बनाई :

public class MapBuilder {

    public static Map<String, Object> build(Object... data) {
        Map<String, Object> result = new LinkedHashMap<>();

        if (data.length % 2 != 0) {
            throw new IllegalArgumentException("Odd number of arguments");
        }

        String key = null;
        Integer step = -1;

        for (Object value : data) {
            step++;
            switch (step % 2) {
                case 0:
                    if (value == null) {
                        throw new IllegalArgumentException("Null key value");
                    }
                    key = (String) value;
                    continue;
                case 1:
                    result.put(key, value);
                    break;
            }
        }

        return result;
    }

}

तब मैंने StringFormatस्ट्रिंग स्वरूपण के लिए कक्षा बनाई :

public final class StringFormat {

    public static String format(String format, Object... args) {
        Map<String, Object> values = MapBuilder.build(args);

        for (Map.Entry<String, Object> entry : values.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            format = format.replace("$" + key, value.toString());
        }

        return format;
    }

}

जो आप इस तरह का उपयोग कर सकते हैं:

String bookingDate = StringFormat.format("From $startDate to $endDate"), 
        "$startDate", formattedStartDate, 
        "$endDate", formattedEndDate
);

1
चेतावनी: क्योंकि लूप मध्यवर्ती प्रतिस्थापन परिणामों को अपने स्वयं के स्वरूपण के रूप में व्यवहार करता है, यह समाधान स्ट्रिंग हमलों को स्वरूपित करने के लिए असुरक्षित है । किसी भी सही समाधान को प्रारूप स्ट्रिंग के माध्यम से एक एकल पास बनाना चाहिए।
200_सेकंड

1

मैंने एक उपयोग / सहायक वर्ग (jdk 8 का उपयोग करके) भी बनाया है जो एक स्ट्रिंग को चर की घटनाओं को बदल सकता है।

इस उद्देश्य के लिए मैंने मिलानकर्ता "परिशिष्ट" विधि का उपयोग किया जो सभी प्रतिस्थापन और केवल एक प्रारूप स्ट्रिंग के प्रभावित भागों पर लूप करता है।

सहायक वर्ग वर्तमान में अच्छी तरह से javadoc प्रलेखित नहीं है। मैं इसे भविष्य में बदल दूंगा;) वैसे भी मैंने सबसे महत्वपूर्ण लाइनों पर टिप्पणी की (मुझे उम्मीद है)।

    public class FormatHelper {

    //Prefix and suffix for the enclosing variable name in the format string.
    //Replace the default values with any you need.
    public static final String DEFAULT_PREFIX = "${";
    public static final String DEFAULT_SUFFIX = "}";

    //Define dynamic function what happens if a key is not found.
    //Replace the defualt exception with any "unchecked" exception type you need or any other behavior.
    public static final BiFunction<String, String, String> DEFAULT_NO_KEY_FUNCTION =
            (fullMatch, variableName) -> {
                throw new RuntimeException(String.format("Key: %s for variable %s not found.",
                                                         variableName,
                                                         fullMatch));
            };
    private final Pattern variablePattern;
    private final Map<String, String> values;
    private final BiFunction<String, String, String> noKeyFunction;
    private final String prefix;
    private final String suffix;

    public FormatHelper(Map<String, String> values) {
        this(DEFAULT_NO_KEY_FUNCTION, values);
    }

    public FormatHelper(
            BiFunction<String, String, String> noKeyFunction, Map<String, String> values) {
        this(DEFAULT_PREFIX, DEFAULT_SUFFIX, noKeyFunction, values);
    }

    public FormatHelper(String prefix, String suffix, Map<String, String> values) {
        this(prefix, suffix, DEFAULT_NO_KEY_FUNCTION, values);
    }

    public FormatHelper(
            String prefix,
            String suffix,
            BiFunction<String, String, String> noKeyFunction,
            Map<String, String> values) {
        this.prefix = prefix;
        this.suffix = suffix;
        this.values = values;
        this.noKeyFunction = noKeyFunction;

        //Create the Pattern and quote the prefix and suffix so that the regex don't interpret special chars.
        //The variable name is a "\w+" in an extra capture group.
        variablePattern = Pattern.compile(Pattern.quote(prefix) + "(\\w+)" + Pattern.quote(suffix));
    }

    public static String format(CharSequence format, Map<String, String> values) {
        return new FormatHelper(values).format(format);
    }

    public static String format(
            CharSequence format,
            BiFunction<String, String, String> noKeyFunction,
            Map<String, String> values) {
        return new FormatHelper(noKeyFunction, values).format(format);
    }

    public static String format(
            String prefix, String suffix, CharSequence format, Map<String, String> values) {
        return new FormatHelper(prefix, suffix, values).format(format);
    }

    public static String format(
            String prefix,
            String suffix,
            BiFunction<String, String, String> noKeyFunction,
            CharSequence format,
            Map<String, String> values) {
        return new FormatHelper(prefix, suffix, noKeyFunction, values).format(format);
    }

    public String format(CharSequence format) {

        //Create matcher based on the init pattern for variable names.
        Matcher matcher = variablePattern.matcher(format);

        //This buffer will hold all parts of the formatted finished string.
        StringBuffer formatBuffer = new StringBuffer();

        //loop while the matcher finds another variable (prefix -> name <- suffix) match
        while (matcher.find()) {

            //The root capture group with the full match e.g ${variableName}
            String fullMatch = matcher.group();

            //The capture group for the variable name resulting from "(\w+)" e.g. variableName
            String variableName = matcher.group(1);

            //Get the value in our Map so the Key is the used variable name in our "format" string. The associated value will replace the variable.
            //If key is missing (absent) call the noKeyFunction with parameters "fullMatch" and "variableName" else return the value.
            String value = values.computeIfAbsent(variableName, key -> noKeyFunction.apply(fullMatch, key));

            //Escape the Map value because the "appendReplacement" method interprets the $ and \ as special chars.
            String escapedValue = Matcher.quoteReplacement(value);

            //The "appendReplacement" method replaces the current "full" match (e.g. ${variableName}) with the value from the "values" Map.
            //The replaced part of the "format" string is appended to the StringBuffer "formatBuffer".
            matcher.appendReplacement(formatBuffer, escapedValue);
        }

        //The "appendTail" method appends the last part of the "format" String which has no regex match.
        //That means if e.g. our "format" string has no matches the whole untouched "format" string is appended to the StringBuffer "formatBuffer".
        //Further more the method return the buffer.
        return matcher.appendTail(formatBuffer)
                      .toString();
    }

    public String getPrefix() {
        return prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public Map<String, String> getValues() {
        return values;
    }
}

आप मानों (या प्रत्यय उपसर्ग या noKeyFunction) के साथ एक विशिष्ट मानचित्र के लिए एक वर्ग उदाहरण बना सकते हैं:

    Map<String, String> values = new HashMap<>();
    values.put("firstName", "Peter");
    values.put("lastName", "Parker");


    FormatHelper formatHelper = new FormatHelper(values);
    formatHelper.format("${firstName} ${lastName} is Spiderman!");
    // Result: "Peter Parker is Spiderman!"
    // Next format:
    formatHelper.format("Does ${firstName} ${lastName} works as photographer?");
    //Result: "Does Peter Parker works as photographer?"

इसके अलावा, आप परिभाषित कर सकते हैं कि क्या होता है यदि मानों की एक कुंजी गायब है (मानचित्र में दोनों प्रकार से गलत चर नाम या मानचित्र में गायब कुंजी) में काम करता है। डिफ़ॉल्ट व्यवहार एक अनियंत्रित अपवाद है (अनियंत्रित क्योंकि मैं डिफ़ॉल्ट jdk8 फ़ंक्शन का उपयोग करता हूं, जो कि कैंट हैंडल के अपवाद की जाँच नहीं करता है जैसे कि:

    Map<String, String> map = new HashMap<>();
    map.put("firstName", "Peter");
    map.put("lastName", "Parker");


    FormatHelper formatHelper = new FormatHelper(map);
    formatHelper.format("${missingName} ${lastName} is Spiderman!");
    //Result: RuntimeException: Key: missingName for variable ${missingName} not found.

आप कंस्ट्रक्टर कॉल में एक कस्टम व्यवहार को परिभाषित कर सकते हैं जैसे:

Map<String, String> values = new HashMap<>();
values.put("firstName", "Peter");
values.put("lastName", "Parker");


FormatHelper formatHelper = new FormatHelper(fullMatch, variableName) -> variableName.equals("missingName") ? "John": "SOMETHING_WRONG", values);
formatHelper.format("${missingName} ${lastName} is Spiderman!");
// Result: "John Parker is Spiderman!"

या इसे डिफ़ॉल्ट पर वापस भेजें कोई महत्वपूर्ण व्यवहार नहीं:

...
    FormatHelper formatHelper = new FormatHelper((fullMatch, variableName) ->   variableName.equals("missingName") ? "John" :
            FormatHelper.DEFAULT_NO_KEY_FUNCTION.apply(fullMatch,
                                                       variableName), map);
...

बेहतर हैंडलिंग के लिए स्थैतिक विधि निरूपण जैसे:

Map<String, String> values = new HashMap<>();
values.put("firstName", "Peter");
values.put("lastName", "Parker");

FormatHelper.format("${firstName} ${lastName} is Spiderman!", map);
// Result: "Peter Parker is Spiderman!"

1

इसे लिखने के समय जावा में कुछ भी नहीं बनाया गया है। मैं आपको अपना स्वयं का कार्यान्वयन लिखने का सुझाव दूंगा। मेरी प्राथमिकता एक सरल धाराप्रवाह बिल्डर इंटरफ़ेस के लिए है, बजाय एक मानचित्र बनाने और इसे कार्य करने के लिए पारित करने के लिए - आप कोड के एक अच्छे सन्निहित चंक के साथ समाप्त होते हैं, उदाहरण के लिए:

String result = new TemplatedStringBuilder("My name is {{name}} and I from {{town}}")
   .replace("name", "John Doe")
   .replace("town", "Sydney")
   .finish();

यहाँ एक सरल कार्यान्वयन है:

class TemplatedStringBuilder {

    private final static String TEMPLATE_START_TOKEN = "{{";
    private final static String TEMPLATE_CLOSE_TOKEN = "}}";

    private final String template;
    private final Map<String, String> parameters = new HashMap<>();

    public TemplatedStringBuilder(String template) {
        if (template == null) throw new NullPointerException();
        this.template = template;
    }

    public TemplatedStringBuilder replace(String key, String value){
        parameters.put(key, value);
        return this;
    }

    public String finish(){

        StringBuilder result = new StringBuilder();

        int startIndex = 0;

        while (startIndex < template.length()){

            int openIndex  = template.indexOf(TEMPLATE_START_TOKEN, startIndex);

            if (openIndex < 0){
                result.append(template.substring(startIndex));
                break;
            }

            int closeIndex = template.indexOf(TEMPLATE_CLOSE_TOKEN, openIndex);

            if(closeIndex < 0){
                result.append(template.substring(startIndex));
                break;
            }

            String key = template.substring(openIndex + TEMPLATE_START_TOKEN.length(), closeIndex);

            if (!parameters.containsKey(key)) throw new RuntimeException("missing value for key: " + key);

            result.append(template.substring(startIndex, openIndex));
            result.append(parameters.get(key));

            startIndex = closeIndex + TEMPLATE_CLOSE_TOKEN.length();
        }

        return result.toString();
    }
}

0

फ़्रीमार्कर , टेम्प्लेटिंग लाइब्रेरी आज़माएं ।

वैकल्पिक शब्द


4
Freemarker? मुझे लगता है कि वह यह जानना चाहते हैं कि सादे जावा में यह कैसे करना है। वैसे भी अगर फ्रीपॉकर संभावित जवाब है तो क्या मैं कह सकता हूं कि जेएसपी भी सही जवाब होगा?
राकेश जुयाल

1
धन्यवाद, लेकिन मेरे काम के लिए यह ओवरकिल की तरह लग रहा है। लेकिन धन्यवाद।
एंडी

1
@ राकेश JSP एक बहुत ही "दृश्य / FE" विशिष्ट बात है। मैंने XML बनाने के लिए अतीत में FreeMarker का उपयोग किया है और कभी-कभी JAVA फ़ाइलों को भी उत्पन्न किया है। एंडी मुझे डर है कि आपको एक उपयोगिता खुद लिखनी होगी (या ऊपर दिए गए एक जैसे)
कन्नन एकनाथ

@ बोरिस कौन सा एक बेहतर फ्रीडमरर बनाम वेग बनाम स्ट्रेंथप्लेट है?
गौरव

1
@gaurav पर एक नज़र डालें stackoverflow.com/questions/3741618/... और dzone.com/articles/...
बोरिस Pavlović

0

https://dzone.com/articles/java-string-format-examples String.format (inputString, [listOfParams]) सबसे आसान तरीका होगा। स्ट्रिंग में प्लेसहोल्डर्स को ऑर्डर द्वारा परिभाषित किया जा सकता है। अधिक जानकारी के लिए दिए गए लिंक की जाँच करें।


0

आपके पास आधिकारिक ICU4J पुस्तकालय पर एक नज़र होनी चाहिए । यह JDK के साथ उपलब्ध एक के समान एक MessageFormat वर्ग प्रदान करता है , लेकिन यह पूर्व नामित प्लेसहोल्डर्स का समर्थन करता है।

इस पृष्ठ पर उपलब्ध अन्य समाधानों के विपरीत। ICU4j ICU प्रोजेक्ट का हिस्सा है जो IBM द्वारा बनाए रखा जाता है और नियमित रूप से अपडेट किया जाता है। इसके अलावा, यह बहुवचन के रूप में उन्नत उपयोग के मामलों का समर्थन करता है और बहुत कुछ।

यहाँ एक कोड उदाहरण है:

MessageFormat messageFormat =
        new MessageFormat("Publication written by {author}.");

Map<String, String> args = Map.of("author", "John Doe");

System.out.println(messageFormat.format(args));

0

जावा में स्ट्रिंग इंटरपोलेशन का उपयोग करने के लिए जावा प्लगिन है (जैसे कोटलिन, जावास्क्रिप्ट में)। समर्थन जावा 8, 9, 10, 11 ... https://github.com/antkorwin/better-strings

स्ट्रिंग लीटर में चर का उपयोग करना:

int a = 3;
int b = 4;
System.out.println("${a} + ${b} = ${a+b}");

अभिव्यक्ति का उपयोग करना:

int a = 3;
int b = 4;
System.out.println("pow = ${a * a}");
System.out.println("flag = ${a > b ? true : false}");

कार्यों का उपयोग करना:

@Test
void functionCall() {
    System.out.println("fact(5) = ${factorial(5)}");
}

long factorial(int n) {
    long fact = 1;
    for (int i = 2; i <= n; i++) {
        fact = fact * i;
    }
    return fact;
}

अधिक जानकारी के लिए, कृपया पढ़ें README।

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