web-dev-qa-db-ja.com

Java文字列内のトークンのセットを置き換える方法は?

次のテンプレート文字列があります:"Hello [Name] Please find attached [Invoice Number] which is due on [Due Date]"

また、名前、請求書番号、期日用の文字列変数もあります。テンプレートのトークンを変数に置き換える最良の方法は何ですか?

(変数にトークンが含まれている場合は、置き換えないでください)。


編集

@laginimainebと@ alan-mooreのおかげで、ここに私の解決策があります:

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();
}
104
Mark

最も効率的な方法は、マッチャーを使用して継続的に式を見つけて置換し、テキストを文字列ビルダーに追加することです。

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();
65
laginimaineb

テンプレートエンジンなどを使用する必要はないと思います。次のように String.format メソッドを使用できます。

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

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

残念ながら、上記の快適なメソッドString.formatはJava 1.5からしか利用できません(これは最近ではかなり標準的なはずですが、あなたは決して知りません)。その代わりに、プレースホルダを置き換えるためにJavaの class MessageFormat を使用することもできます。

'{number}'の形式のプレースホルダーをサポートしているため、メッセージは「こんにちは{0} {2}に期限のある添付{1}を見つけてください」のようになります。これらの文字列は、ResourceBundlesを使用して簡単に外部化できます(たとえば、複数のロケールでローカライズするため)。置換は、MessageFormatクラスのstatic'format 'メソッドを使用して行われます。

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));
42
A. Knauf

Apache Velocityのようなテンプレートライブラリを使用してみてください。

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);
    }
}

出力は次のようになります。

こんにちはマーク。 2009年6月6日に提出される添付の請求書42123を見つけてください。
41
hallidave

複雑なテンプレートの置換にテンプレートライブラリを使用できます。

FreeMarkerは非常に良い選択です。

http://freemarker.sourceforge.net/

しかし、単純なタスクには、役立つユーティリティクラスがあります。

org.Apache.commons.lang3.text.StrSubstitutor

非常に強力で、カスタマイズ可能で、使いやすいです。

このクラスはテキストを受け取り、その中のすべての変数を置き換えます。変数のデフォルト定義は$ {variableName}です。プレフィックスとサフィックスは、コンストラクターとsetメソッドを介して変更できます。

通常、変数値はマップから解決されますが、システムプロパティから、またはカスタム変数リゾルバーを提供することによっても解決できます。

たとえば、システム環境変数をテンプレート文字列に置き換える場合のコードは次のとおりです。

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);
    }
}
23
Li Ying
System.out.println(MessageFormat.format("Hello {0}! You have {1} messages", "Join",10L));

出力: Hello Join! 10件のメッセージがあります」

16
user2845137
String.format("Hello %s Please find attached %s which is due on %s", name, invoice, date)
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());
}

Stringを反復処理して、マップ内の要素を見つけることもできます。しかし、[]を検索する文字列を解析する必要があるため、これはもう少し複雑です。パターンとマッチャーを使用して正規表現でそれを行うことができます。

8
Ricardo Marimon

$ {variable}スタイルのトークンを置き換えるための私のソリューション(ここの回答とSpring UriTemplateに触発された):

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();
}
3
mihu86

ご参考までに

新しい言語のKotlinでは、ソースコードで「文字列テンプレート」を直接使用できます。サードパーティのライブラリまたはテンプレートエンジンで変数の置換を行う必要はありません。

これは、言語自体の機能です。

参照: https://kotlinlang.org/docs/reference/basic-types.html#string-templates

1
Li Ying
1
Marcel

私は使った

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

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

過去に、 StringTemplate および Groovy Templates でこの種の問題を解決しました。

最終的に、テンプレートエンジンを使用するかどうかは、次の要因に基づいて決定する必要があります。

  • これらのテンプレートの多くをアプリケーションに入れますか?
  • アプリケーションを再起動せずにテンプレートを変更する機能が必要ですか?
  • これらのテンプレートは誰が管理しますか?プロジェクトに関係するJavaプログラマーまたはビジネスアナリストですか?
  • 変数の値に基づく条件付きテキストなど、テンプレートにロジックを配置する機能が必要ですか?
  • 他のテンプレートをテンプレートに含める機能が必要ですか?

上記のいずれかがプロジェクトに当てはまる場合は、テンプレートエンジンの使用を検討します。テンプレートエンジンのほとんどは、この機能などを提供します。

0
Francois Gravel

Apache Commons Libraryでは、単に Stringutils.replaceEach を使用できます。

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

ドキュメント から:

別のストリング内のストリングのすべての出現を置き換えます。

このメソッドに渡されるnull参照はノーオペレーションです。または、「検索文字列」または「置換する文字列」がnullの場合、その置換は無視されます。これは繰り返されません。置換を繰り返すには、オーバーロードされたメソッドを呼び出します。

 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"
0
AR1