জাভা স্ট্রিংয়ে টোকেনের সেট কীভাবে প্রতিস্থাপন করবেন?


106

আমি নিম্নলিখিত টেমপ্লেট স্ট্রিং আছে: "Hello [Name] Please find attached [Invoice Number] which is due on [Due Date]"

নাম, চালান নম্বর এবং নির্ধারিত তারিখের জন্য আমার কাছে স্ট্রিং ভেরিয়েবলগুলিও রয়েছে - ভেরিয়েবলগুলির সাথে টেমপ্লেটে টোকেনগুলি প্রতিস্থাপনের সেরা উপায় কী?

(দ্রষ্টব্য যে কোনও ভেরিয়েবলের একটি টোকেন ধারণ করে থাকলে তা প্রতিস্থাপন করা উচিত নয়)।


সম্পাদনা

@ লাজিনিমায়েব এবং @ অ্যালান-মুরকে ধন্যবাদ দিয়ে, এখানে আমার সমাধানটি দেওয়া হয়েছে:

public static String replaceTokens(String text, 
                                   Map<String, String> replacements) {
    Pattern pattern = Pattern.compile("\\[(.+?)\\]");
    Matcher matcher = pattern.matcher(text);
    StringBuffer buffer = new StringBuffer();

    while (matcher.find()) {
        String replacement = replacements.get(matcher.group(1));
        if (replacement != null) {
            // matcher.appendReplacement(buffer, replacement);
            // see comment 
            matcher.appendReplacement(buffer, "");
            buffer.append(replacement);
        }
    }
    matcher.appendTail(buffer);
    return buffer.toString();
}

একটি বিষয় লক্ষণীয়, তবে স্ট্রিংবুফারটি কেবল সিংক্রোনাইজড স্ট্রিংবুডার হিসাবে একই। যাইহোক, উদাহরণস্বরূপ আপনার স্ট্রিং বিল্ডিংটি সিঙ্ক্রোনাইজ করার দরকার নেই আপনি স্ট্রিংবিল্ডার ব্যবহারের চেয়ে ভাল হতে পারেন (যদিও লকগুলি অর্জন প্রায় শূন্য-ব্যয়ের ক্রিয়াকলাপ)।
লেগিনিমাইনব

1
দুর্ভাগ্যক্রমে, আপনাকে স্ট্রিংবাফার এই ক্ষেত্রে ব্যবহার করতে হবে; এটি অ্যাপেন্ডেক্সএক্সএক্সএক্স () পদ্ধতিগুলি কী প্রত্যাশা করে। তারা জাভা ৪ এর পর থেকে রয়েছে, এবং স্ট্রিংবিল্ডার জাভা ৫ পর্যন্ত যোগ করা হয়নি। আপনি যেমনটি বলেছেন, এটি কোনও বড় বিষয় নয়, কেবল বিরক্তিকর।
অ্যালান মুর

4
আরও একটি জিনিস: অ্যাপেন্ডেল রিপ্লেসমেন্ট (), রিপ্লেক্স এক্সএক্সএক্সএক্স () পদ্ধতির মতো ক্যাপচার-গ্রুপের রেফারেন্সগুলি $ 1, $ 2 ইত্যাদির জন্য অনুসন্ধান করে এবং সম্পর্কিত ক্যাপচার গ্রুপগুলির পাঠ্য দ্বারা এগুলি প্রতিস্থাপন করে। যদি আপনার প্রতিস্থাপন পাঠ্যে ডলারের চিহ্ন বা ব্যাকস্ল্যাশ থাকতে পারে (যা ডলার চিহ্ন থেকে বাঁচতে ব্যবহৃত হয়), আপনার সমস্যা হতে পারে। এটির মোকাবিলার সহজতম উপায় হ'ল উপরের কোডটিতে আমি যেমন কাজ করেছি তেমন দুটি পদক্ষেপে অ্যাপেনড অপারেশনটি ভাঙ্গা।
অ্যালান মুর

অ্যালান - আপনি খুব স্পষ্ট যে এটি। আমি ভাবিনি যে এত সাধারণ সমস্যা সমাধান করা এত কঠিন হবে!
চিহ্নিত করুন

উত্তর:


65

সর্বাধিক দক্ষ উপায় হ'ল ম্যাচার ব্যবহার করে ক্রমাগত অভিব্যক্তিগুলি খুঁজে পেতে এবং সেগুলি প্রতিস্থাপন করতে পারেন, তারপরে একটি স্ট্রিং বিল্ডারে পাঠ্য সংযোজন করুন:

Pattern pattern = Pattern.compile("\\[(.+?)\\]");
Matcher matcher = pattern.matcher(text);
HashMap<String,String> replacements = new HashMap<String,String>();
//populate the replacements map ...
StringBuilder builder = new StringBuilder();
int i = 0;
while (matcher.find()) {
    String replacement = replacements.get(matcher.group(1));
    builder.append(text.substring(i, matcher.start()));
    if (replacement == null)
        builder.append(matcher.group(0));
    else
        builder.append(replacement);
    i = matcher.end();
}
builder.append(text.substring(i, text.length()));
return builder.toString();

10
ম্যাচারের অ্যাপেন্ডেল রিপ্লেসমেন্ট () এবং অ্যাপেন্ডেল টেল () পদ্ধতিগুলি ব্যবহার না করে মিলে যায় না এমনটি বাদ দিয়ে আমি এটি করব; এটি হাতে হাতে করার দরকার নেই।
অ্যালান মুর

5
আসলে অ্যাপেনড্রেসপ্লেসমেন্ট () এবং অ্যাপেন্টেল টেল () পদ্ধতিগুলির জন্য স্ট্রিংবফার প্রয়োজন, যা স্নক্রোনাইজড (যা এখানে কোনও কাজে আসে না)। প্রদত্ত উত্তরে একটি স্ট্রিংবিল্ডার ব্যবহার করা হয়েছে, যা আমার পরীক্ষায় 20% দ্রুত faster
ডাব

103

আমি সত্যিই ভাবি না যে এর জন্য আপনাকে একটি টেম্প্লেটিং ইঞ্জিন বা এর মতো কিছু ব্যবহার করা দরকার। আপনি String.formatপদ্ধতিটি যেমন ব্যবহার করতে পারেন :

String template = "Hello %s Please find attached %s which is due on %s";

String message = String.format(template, name, invoiceNumber, dueDate);

4
এর একটি নেতিবাচক
দিকটি

আরেকটি হ'ল আপনি নিজের প্রতিস্থাপন টোকেন ফর্ম্যাটটি নির্দিষ্ট করতে পারবেন না।
ফ্রাঞ্জ ডি।

অন্যটি হ'ল এটি গতিশীলভাবে কাজ করে না, কী / মানগুলির একটি ডেটাসেট রাখতে সক্ষম হয়ে কোনও স্ট্রিংয়ের সাথে প্রয়োগ করতে পারে
ব্র্যাড পার্কস

43

দুর্ভাগ্যক্রমে উপরে বর্ণিত আরামদায়ক পদ্ধতি স্ট্রিং.ফর্ম্যাটটি কেবল জাভা 1.5 দিয়ে শুরু হবে (যা আজকাল বেশ স্ট্যান্ডার্ড হওয়া উচিত, তবে আপনি কখনই জানেন না)। পরিবর্তে আপনি স্থানধারীদের প্রতিস্থাপনের জন্য জাভা শ্রেণীর বার্তা ফর্ম্যাট ব্যবহার করতে পারেন ।

এটি '{সংখ্যা}' আকারে স্থানধারীদের সমর্থন করে, তাই আপনার বার্তাটি "হ্যালো {0 like মত লাগবে দয়া করে সংযুক্ত find 1} সন্ধান করুন যা {2}" এর কারণে প্রযোজ্য। "।" এই স্ট্রিংগুলি সহজেই রিসোর্সবাণ্ডলগুলি ব্যবহার করে বহিরাগত করা যায় (যেমন একাধিক লোকাল সহ স্থানীয়করণের জন্য)। প্রতিস্থাপনটি স্ট্যাটিক 'ফর্ম্যাট' ক্লাসের বার্তা ফর্ম্যাট পদ্ধতি ব্যবহার করে করা হবে:

String msg = "Hello {0} Please find attached {1} which is due on {2}";
String[] values = {
  "John Doe", "invoice #123", "2009-06-30"
};
System.out.println(MessageFormat.format(msg, values));

3
আমি মেসেজফর্ম্যাটটির নামটি মনে করতে পারিনি, এবং এই উত্তরটি খুঁজে পেতে আমাকে গুগলিংয়ের কতটা নির্বোধ ছিল তা একধরণের মূর্খতা। প্রত্যেকেই এটি স্ট্রিং.ফর্ম্যাট এর মতো কাজ করে বা তৃতীয় পক্ষ ব্যবহার করে, এই অবিশ্বাস্যভাবে দরকারী ইউটিলিটিটি ভুলে।
প্যাট্রিক

1
এটি 2004 সাল থেকে পাওয়া যায় - কেন আমি কেবলমাত্র এখনই শিখছি, 2017 সালে? আমি কিছু কোডকে রিফ্যাক্টর করছি যা এস এর মধ্যে আচ্ছাদিত StringBuilder.append()ছিল এবং আমি ভাবছিলাম "অবশ্যই এর থেকে আরও ভাল উপায় আছে ... আরও কিছু পাইথোনিক ..." - এবং পবিত্র বোকা, আমি মনে করি যে এই পদ্ধতিটি পাইথনের বিন্যাস পদ্ধতিতে পূর্ববর্তী হতে পারে। আসলে ... এটি ২০০২ এরও বেশি পুরনো হতে পারে ... এটি আসলে কখন আবির্ভূত হয়েছিল তা আমি খুঁজে পাই না ...
আর্টঅফ ওয়ারফেয়ার

42

আপনি অ্যাপাচি বেগের মতো একটি টেম্প্লেটিং লাইব্রেরি ব্যবহার করে দেখতে পারেন।

http://velocity.apache.org/

এখানে একটি উদাহরণ:

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import java.io.StringWriter;

public class TemplateExample {
    public static void main(String args[]) throws Exception {
        Velocity.init();

        VelocityContext context = new VelocityContext();
        context.put("name", "Mark");
        context.put("invoiceNumber", "42123");
        context.put("dueDate", "June 6, 2009");

        String template = "Hello $name. Please find attached invoice" +
                          " $invoiceNumber which is due on $dueDate.";
        StringWriter writer = new StringWriter();
        Velocity.evaluate(context, writer, "TemplateName", template);

        System.out.println(writer);
    }
}

আউটপুটটি হবে:

হ্যালো মার্ক দয়া করে সংযুক্ত চালানটি 42123 সন্ধান করুন যা 6 জুন, 2009-এ বকেয়া রয়েছে।

আমি অতীতে বেগ ব্যবহার করেছি। দুর্দান্ত কাজ করে।
হার্ডওয়্যারগুই

4
সম্মত হন, চাকাটিকে পুনরায় উদ্বেগ কেন করা হচ্ছে
অবজেক্ট

6
এর মতো সাধারণ কাজের জন্য একটি পুরো লাইব্রেরি ব্যবহার করা কিছুটা ওভারকিল। বেগের আরও অনেক বৈশিষ্ট্য রয়েছে এবং আমি দৃ strongly়ভাবে বিশ্বাস করি যে এটি এই জাতীয় কোনও সহজ কাজের জন্য উপযুক্ত নয়।
আন্দ্রেই সিওবানু

24

জটিল টেম্পলেট প্রতিস্থাপনের জন্য আপনি টেমপ্লেট লাইব্রেরি ব্যবহার করতে পারেন।

ফ্রিমার্কার একটি খুব ভাল পছন্দ।

http://freemarker.sourceforge.net/

তবে সাধারণ কাজের জন্য একটি সাধারণ ইউটিলিটি শ্রেণি আপনাকে সহায়তা করতে পারে।

org.apache.commons.lang3.text.StrSubstitutor

এটি অত্যন্ত শক্তিশালী, অনুকূলিতকরণযোগ্য এবং সহজেই ব্যবহারযোগ্য।

এই শ্রেণিটি পাঠ্যের একটি অংশ নেয় এবং এর মধ্যে সমস্ত ভেরিয়েবলকে প্রতিস্থাপন করে। একটি ভেরিয়েবলের ডিফল্ট সংজ্ঞা $ {ভেরিয়েবলনেম N} উপসর্গ এবং প্রত্যয়টি কনস্ট্রাক্টর এবং সেট পদ্ধতির মাধ্যমে পরিবর্তন করা যেতে পারে।

পরিবর্তনশীল মানগুলি সাধারণত কোনও মানচিত্র থেকে সমাধান করা হয় তবে এটি সিস্টেমের বৈশিষ্ট্য থেকে বা কাস্টম ভেরিয়েবল রিসলভার সরবরাহ করেও সমাধান করা যেতে পারে।

উদাহরণস্বরূপ, আপনি যদি সিস্টেম এনভায়রনমেন্ট ভেরিয়েবলকে টেম্পলেট স্ট্রিংয়ের পরিবর্তে নিতে চান তবে কোডটি এখানে:

public class SysEnvSubstitutor {
    public static final String replace(final String source) {
        StrSubstitutor strSubstitutor = new StrSubstitutor(
                new StrLookup<Object>() {
                    @Override
                    public String lookup(final String key) {
                        return System.getenv(key);
                    }
                });
        return strSubstitutor.replace(source);
    }
}

2
org.apache.commons.lang3.text.StrSubstitutor আমার জন্য দুর্দান্ত কাজ করেছে
PS0604

17
System.out.println(MessageFormat.format("Hello {0}! You have {1} messages", "Join",10L));

আউটপুট: হ্যালো যোগ দিন! আপনার 10 টি বার্তা রয়েছে "


2
জন যখনই আমার "স্প্যাম" ফোল্ডারটি দীর্ঘায়িত হয় ততবারই আমি তার বার্তা পরিষ্কারভাবে পরীক্ষা করে দেখি।
হেমেলস

9

এটি আপনার প্রতিস্থাপন করতে চান এমন প্রকৃত ডেটা কোথায় রয়েছে তা নির্ভর করে। আপনার এই জাতীয় মানচিত্র থাকতে পারে:

Map<String, String> values = new HashMap<String, String>();

প্রতিস্থাপন করা যেতে পারে এমন সমস্ত ডেটা রয়েছে। তারপরে আপনি মানচিত্রে পুনরাবৃত্তি করতে পারেন এবং স্ট্রিংয়ের সমস্ত কিছু নীচে পরিবর্তন করতে পারেন:

String s = "Your String with [Fields]";
for (Map.Entry<String, String> e : values.entrySet()) {
  s = s.replaceAll("\\[" + e.getKey() + "\\]", e.getValue());
}

আপনি স্ট্রিং দিয়ে পুনরাবৃত্তি করতে পারেন এবং মানচিত্রে উপাদানগুলি খুঁজে পেতে পারেন। তবে এটি খানিকটা জটিল কারণ আপনার স্ট্রিং সন্ধান [=] অনুসন্ধান করার জন্য পার্স করা দরকার। প্যাটার্ন এবং ম্যাচার ব্যবহার করে আপনি এটি নিয়মিত প্রকাশের মাধ্যমে করতে পারেন।


9
String.format("Hello %s Please find attached %s which is due on %s", name, invoice, date)

1
ধন্যবাদ - কিন্তু আমার ক্ষেত্রে টেমপ্লেট স্ট্রিং ব্যবহারকারী দ্বারা, পরিবর্তন করা যাবে তাই আমি টোকেন আদেশ নিশ্চিত হতে পারে না
মার্ক

3

Solution {পরিবর্তনশীল} স্টাইলের টোকেনগুলি প্রতিস্থাপনের জন্য আমার সমাধান (উত্তরগুলি এখানে এবং স্প্রিং ইউরিপ্যাম্পেট দ্বারা অনুপ্রাণিত):

public static String substituteVariables(String template, Map<String, String> variables) {
    Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}");
    Matcher matcher = pattern.matcher(template);
    // StringBuilder cannot be used here because Matcher expects StringBuffer
    StringBuffer buffer = new StringBuffer();
    while (matcher.find()) {
        if (variables.containsKey(matcher.group(1))) {
            String replacement = variables.get(matcher.group(1));
            // quote to work properly with $ and {,} signs
            matcher.appendReplacement(buffer, replacement != null ? Matcher.quoteReplacement(replacement) : "null");
        }
    }
    matcher.appendTail(buffer);
    return buffer.toString();
}


1

অ্যাপাচি কমন্স লাইব্রেরি সহ, আপনি কেবল স্ট্রিংগিলস.রেপ্সএইচ ব্যবহার করতে পারেন :

public static String replaceEach(String text,
                             String[] searchList,
                             String[] replacementList)

ডকুমেন্টেশন থেকে :

স্ট্রিংয়ের সমস্ত উপস্থিতি অন্য স্ট্রিংয়ের মধ্যে প্রতিস্থাপন করে।

এই পদ্ধতিতে পাস করা একটি নাল রেফারেন্স হ'ল অপ-অপশন, বা যদি কোনও "অনুসন্ধানের স্ট্রিং" বা "স্ট্রিং প্রতিস্থাপন" নਾਲ হয়, তবে প্রতিস্থাপনটিকে অগ্রাহ্য করা হবে। এটি পুনরাবৃত্তি করবে না। পুনঃস্থাপনের জন্য, ওভারলোডেড পদ্ধতিটি কল করুন call

 StringUtils.replaceEach(null, *, *)        = null

  StringUtils.replaceEach("", *, *)          = ""

  StringUtils.replaceEach("aba", null, null) = "aba"

  StringUtils.replaceEach("aba", new String[0], null) = "aba"

  StringUtils.replaceEach("aba", null, new String[0]) = "aba"

  StringUtils.replaceEach("aba", new String[]{"a"}, null)  = "aba"

  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"

  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"

  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
  (example of how it does not repeat)

StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"

1

অবগতির জন্য

নতুন ভাষায় কোটলিনে, আপনি সরাসরি আপনার উত্স কোডে "স্ট্রিং টেম্পলেট" ব্যবহার করতে পারেন, কোনও তৃতীয় পক্ষের গ্রন্থাগার বা টেম্পলেট ইঞ্জিনের পরিবর্তনশীল প্রতিস্থাপনের প্রয়োজন নেই।

এটি ভাষার নিজস্ব বৈশিষ্ট্য।

দেখুন: https://kotlinlang.org/docs/references/basic-tyype.html#string-templates


0

অতীতে, আমি স্ট্রিংটেম্পলেট এবং গ্রোভী টেম্পলেটগুলির সাথে এই জাতীয় সমস্যার সমাধান করেছি ।

শেষ পর্যন্ত, একটি টেম্পলেটিং ইঞ্জিন ব্যবহার করার সিদ্ধান্ত নেওয়া নিম্নলিখিত বিষয়গুলির উপর ভিত্তি করে হওয়া উচিত:

  • আপনি অ্যাপ্লিকেশন এই টেম্পলেট অনেক আছে?
  • অ্যাপ্লিকেশনটি পুনরায় আরম্ভ না করে আপনার কী টেমপ্লেটগুলি সংশোধন করার দক্ষতার প্রয়োজন?
  • কে এই টেমপ্লেটগুলি বজায় রাখবে? কোন জাভা প্রোগ্রামার বা কোনও ব্যবসায় বিশ্লেষক এই প্রকল্পের সাথে জড়িত?
  • ভেরিয়েবলের মানগুলির উপর ভিত্তি করে শর্তসাপেক্ষ পাঠ্যের মতো আপনার টেম্পলেটগুলিতে যুক্তি যুক্ত করার দক্ষতার কি দরকার হবে?
  • আপনার কি কোনও টেম্পলেটে অন্যান্য টেমপ্লেট অন্তর্ভুক্ত করার দক্ষতার প্রয়োজন হবে?

উপরের যে কোনওটি যদি আপনার প্রকল্পে প্রযোজ্য হয় তবে আমি একটি টেম্প্লেটিং ইঞ্জিন ব্যবহার করে বিবেচনা করব, যার বেশিরভাগই এই কার্যকারিতা সরবরাহ করে এবং আরও অনেক কিছু করে।


0

আমি ব্যবহার করতাম

String template = "Hello %s Please find attached %s which is due on %s";

String message = String.format(template, name, invoiceNumber, dueDate);

2
এটি কাজ করবে, তবে আমার ক্ষেত্রে টেমপ্লেট স্ট্রিংটি ব্যবহারকারী কাস্টমাইজ করতে পারে, তাই টোকেনগুলি কোন ক্রমে উপস্থিত হবে তা আমি জানি না।
চিহ্নিত করুন

0

নিম্নলিখিতটি ফর্মের ভেরিয়েবলগুলি প্রতিস্থাপন করে <<VAR>>, মানচিত্র থেকে দেখানো মানগুলির সাথে। আপনি এটি এখানে অনলাইনে পরীক্ষা করতে পারেন

উদাহরণস্বরূপ, নিম্নলিখিত ইনপুট স্ট্রিং সহ

BMI=(<<Weight>>/(<<Height>>*<<Height>>)) * 70
Hi there <<Weight>> was here

এবং নিম্নলিখিত পরিবর্তনশীল মান

Weight, 42
Height, HEIGHT 51

নিম্নলিখিত আউটপুট

BMI=(42/(HEIGHT 51*HEIGHT 51)) * 70

Hi there 42 was here

এখানে কোড

  static Pattern pattern = Pattern.compile("<<([a-z][a-z0-9]*)>>", Pattern.CASE_INSENSITIVE);

  public static String replaceVarsWithValues(String message, Map<String,String> varValues) {
    try {
      StringBuffer newStr = new StringBuffer(message);
      int lenDiff = 0;
      Matcher m = pattern.matcher(message);
      while (m.find()) {
        String fullText = m.group(0);
        String keyName = m.group(1);
        String newValue = varValues.get(keyName)+"";
        String replacementText = newValue;
        newStr = newStr.replace(m.start() - lenDiff, m.end() - lenDiff, replacementText);
        lenDiff += fullText.length() - replacementText.length();
      }
      return newStr.toString();
    } catch (Exception e) {
      return message;
    }
  }


  public static void main(String args[]) throws Exception {
      String testString = "BMI=(<<Weight>>/(<<Height>>*<<Height>>)) * 70\n\nHi there <<Weight>> was here";
      HashMap<String,String> values = new HashMap<>();
      values.put("Weight", "42");
      values.put("Height", "HEIGHT 51");
      System.out.println(replaceVarsWithValues(testString, values));
  }

এবং যদিও অনুরোধ করা হয়নি, আপনি আপনার অ্যাপ্লিকেশন থেকে সম্পত্তি সহ স্ট্রিংয়ে ভেরিয়েবলগুলি প্রতিস্থাপন করতে অনুরূপ পন্থা ব্যবহার করতে পারেন rop

private static Pattern patternMatchForProperties =
      Pattern.compile("[$][{]([.a-z0-9_]*)[}]", Pattern.CASE_INSENSITIVE);

protected String replaceVarsWithProperties(String message) {
    try {
      StringBuffer newStr = new StringBuffer(message);
      int lenDiff = 0;
      Matcher m = patternMatchForProperties.matcher(message);
      while (m.find()) {
        String fullText = m.group(0);
        String keyName = m.group(1);
        String newValue = System.getProperty(keyName);
        String replacementText = newValue;
        newStr = newStr.replace(m.start() - lenDiff, m.end() - lenDiff, replacementText);
        lenDiff += fullText.length() - replacementText.length();
      }
      return newStr.toString();
    } catch (Exception e) {
      return message;
    }
  }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.