Java 如何用 SimpleDateFormat 模式替换字符串中的占位符

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16436027/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 06:55:00  来源:igfitidea点击:

How to replace a placeholder in a String with a SimpleDateFormat Pattern

javaapache-commonsspring-el

提问by d0x

In a given String like this

在这样的给定字符串中

".../uploads/${customer}/${dateTime('yyyyMMdd')}/report.pdf"

I need to replace a customerand a yyyyMMddtimestamp.

我需要替换一个customer和一个yyyyMMdd时间戳。

To replace the customerplaceholder, I could use the StrSubstitutorfrom Apache Commons. But how to replace the SimpleDateFormat? We are running in a Spring enviourment, so maybe Spring ELis an option?

要替换customer占位符,我可以使用StrSubstitutor来自 Apache Commons 的。但是如何更换SimpleDateFormat呢?我们在 Spring 环境中运行,所以也许Spring EL是一种选择?

The markup for the placeholders is not fixed, it is ok if another library needs syntactical changes.

占位符的标记不是固定的,如果另一个库需要语法更改也没关系。

This small tests shows the problem:

这个小测试显示了问题:

SimpleDateFormat            formatter   = new SimpleDateFormat("yyyyMMdd");

String                      template    = ".../uploads/${customer}/${dateTime('yyyyMMdd')}/report.pdf";

@Test
public void shouldResolvePlaceholder()
{
    final Map<String, String> model = new HashMap<String, String>();
    model.put("customer", "Mr. Foobar");

    final String filledTemplate = StrSubstitutor.replace(this.template, model);

    assertEquals(".../uploads/Mr. Foobar/" + this.formatter.format(new Date()) + "/report.pdf", filledTemplate);
}

回答by NilsH

Why don't you use MessageFormatinstead?

你为什么不MessageFormat改用?

String result = MessageFormat.format(".../uploads/{0}/{1,date,yyyyMMdd}/report.pdf", customer, date);

Or with String.format

或与 String.format

String result = String.format(".../uploads/%1$s/%2$tY%2$tm%2$td/report.pdf", customer, date);

回答by Walery Strauch

As NilsH suggested MessageFormat is really nice for this purpose. To have named variables you can hide MessageFormat behind your class:

由于 NilsH 建议 MessageFormat 非常适合此目的。要拥有命名变量,您可以将 MessageFormat 隐藏在您的类后面:

public class FormattedStrSubstitutor {
    public static String formatReplace(Object source, Map<String, String> valueMap) {
        for (Map.Entry<String, String> entry : valueMap.entrySet()) {   
            String val = entry.getValue();
            if (isPlaceholder(val)) {
                val = getPlaceholderValue(val);
                String newValue = reformat(val);

                entry.setValue(newValue);
            }
        }

        return new StrSubstitutor(valueMap).replace(source);
    }

    private static boolean isPlaceholder(String isPlaceholder) {
        return isPlaceholder.startsWith("${");
    }

    private static String getPlaceholderValue(String val) {
        return val.substring(2, val.length()-1);
    }

    private static String reformat(String format) {
        String result = MessageFormat.format("{0,date," + format + "}", new Date());

        return result;
    }
}

And you have to adjust your testcase:

你必须调整你的测试用例:

SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");

String template = ".../uploads/${customer}/${dateTime}/report.pdf";

@Test
public void shouldResolvePlaceholder() {
    final Map<String, String> model = new HashMap<String, String>();
    model.put("customer", "Mr. Foobar");
    model.put("dateTime", "${yyyyMMdd}");

    final String filledTemplate = FormattedStrSubstitutor.formatReplace(this.template,
        model);

    assertEquals(".../uploads/Mr. Foobar/" + this.formatter.format(new Date())
        + "/report.pdf", filledTemplate);
}

I have removed generics and replace those with String. Also isPlaceholderand getPlaceholderValueis hardcoded and expect ${value}syntax.

我已经删除了泛型并将其替换为 String。而且isPlaceholderandgetPlaceholderValue是硬编码的并且期望${value}语法。

But this is just just the idea to solve your problem. To do this can use the methods from StrSubstitutor(just use is or make FormattedStrSubstitutor extends StrSubstitutor).

但这只是解决您的问题的想法。要做到这一点,可以使用来自StrSubstitutor(只需使用 is 或 make FormattedStrSubstitutor extends StrSubstitutor)的方法。

Also you can use for example $d{value}for date formatting and $foo{value}for foo formatting.

您也可以使用例如$d{value}用于日期格式和$foo{value}用于 foo 格式。

UPDATE

更新

Could not sleep without full solution. You can add this method to FormattedStrSubstitutorclass:

没有完整的解决方案就无法入睡。您可以将此方法添加到FormattedStrSubstitutor类:

public static String replace(Object source,
        Map<String, String> valueMap) {

    String staticResolved = new StrSubstitutor(valueMap).replace(source);

    Pattern p = Pattern.compile("(\$\{date)(.*?)(\})");
    Matcher m = p.matcher(staticResolved);

    String dynamicResolved = staticResolved;
    while (m.find()) {
        String result = MessageFormat.format("{0,date" + m.group(2) + "}",
                new Date());

        dynamicResolved = dynamicResolved.replace(m.group(), result);
    }

    return dynamicResolved;
}

Your testcase is like in your question (small changes in placeholder):

你的测试用例就像你的问题(占位符的小变化):

SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");

String template = ".../uploads/${customer}/${date,yyyyMMdd}/report.pdf";

@Test
public void shouldResolvePlaceholder() {
    final Map<String, String> model = new HashMap<String, String>();
    model.put("customer", "Mr. Foobar");

    final String filledTemplate =  FormattedStrSubstitutor.replace(this.template,
            model);

    assertEquals(
            ".../uploads/Mr. Foobar/" + this.formatter.format(new Date())
                    + "/report.pdf", filledTemplate);
}

Same limitation as before; no generics and fix prefix and suffix for placeholder.

与以前相同的限制;没有泛型并修复占位符的前缀和后缀。

回答by anydoby

Looks like it's as simple as this?

看起来就这么简单?

static final Pattern DOLLARS = Pattern.compile("\$\{([^}]+)}");

public static String resolve(String string, Map<String,String> config) {
    StringBuilder builder = new StringBuilder();
    Matcher matcher = DOLLARS.matcher(string);
    int start = 0;
    while (matcher.find(start)) {
        builder.append(string.substring(start, matcher.start()));
        String property = matcher.group(1);
        String value = config.get(property);
        builder.append(value);
        start = matcher.end();
    }
    builder.append(string.substring(start));
    return builder.toString();
}