有没有办法在 Java 中呈现像 'Hello, %(name)s' % 'name':'Felix' 这样的字符串?

Posted

技术标签:

【中文标题】有没有办法在 Java 中呈现像 \'Hello, %(name)s\' % \'name\':\'Felix\' 这样的字符串?【英文标题】:Is there a way to render a string like 'Hello, %(name)s' % 'name':'Felix' in Java?有没有办法在 Java 中呈现像 'Hello, %(name)s' % 'name':'Felix' 这样的字符串? 【发布时间】:2013-07-26 04:20:29 【问题描述】:

在 Python 中我们可以轻松做到这一点:

data = 'name':'Felix'
s = 'Hello, %(name)s' % data
s
'Hello, Felix'

在 Java 中是否有类似的方式来实现相同的东西?

PS: 对不起,不清楚的问题。用例是:我们有一个存储键值的映射,模板只需要在映射中指定一个键,那么键的值就会在模板中键所在的位置。

【问题讨论】:

【参考方案1】:

在这个用例下,您需要像velocity或freemarker这样的模板引擎来使用类似Map的数据结构来呈现字符串模板,java中没有内置模块可以做到这一点。像这样(有速度):

public static void main(String[] args) 
    Context context = new VelocityContext();
    context.put("appid", "9876543d1");
    context.put("ds", "2013-09-11");
    StringWriter sw = new StringWriter();
    String template = "APPID is $appid and DS is $ds";
    Velocity.evaluate(context, sw, "velocity", template);
    System.out.println(sw.toString());

【讨论】:

【参考方案2】:

如果你只想用字符串替换字符串,那么我答案第二部分的代码会更好

Java Formatter 类不支持 %(key)s 形式,但您可以使用 %index$s,其中索引从 1 开始计数,如本例所示

System.out.format("%3$s, %2$s, %1s", "a", "b", "c");
// indexes                            1    2    3

输出:

c, b, a

所以你需要做的就是创建一些包含模式中使用的值的数组,并将键名更改为其对应的索引(增加 1,因为 Formatter 使用的第一个索引写为1$ 而不是0$ 之类的我们期望数组索引)。

这里是为你做的方法示例

// I made this Pattern static and put it outside of method to compile it only once,
// also it will match every (xxx) that has % before it, but wont include %
static Pattern formatPattern = Pattern.compile("(?<=%)\\(([^)]+)\\)");

public static String format(String pattern, Map<String, ?> map) 
    StringBuffer sb = new StringBuffer();
    List<Object> valuesList = new ArrayList<>();

    Matcher m = formatPattern.matcher(pattern);

    while (m.find()) 

        String key = m.group(1);//group 1 contains part inside parenthesis

        Object value = map.get(key);
        // If map doesn't contain key, value will be null.
        // If you want to react somehow to null value like throw some
        // Exception
        // now is the good time.

        if (valuesList.contains(value)) 
            m.appendReplacement(sb, (valuesList.indexOf(value) + 1) + "\\$");
         else 
            valuesList.add(value);
            m.appendReplacement(sb, valuesList.size() + "\\$");
        
    
    m.appendTail(sb);

    return String.format(sb.toString(), valuesList.toArray());

用法

Map<String, Object> map = new HashMap<>();
map.put("name", "Felix");
map.put("age", 70);

String myPattern = 
        "Hi %(emptyKey)s! My name is %(name)s %(name)s and I am %(age)s years old";
System.out.println(format(myPattern, map));

输出:

Hi null! My name is Felix Felix and I am 70 years old

如您所见,您可以多次使用相同的键(在我们的例子中为 name),如果您的地图不包含字符串模式中使用的键(如 emptyKey),它将被替换为 null


以上版本旨在让您设置数据类型,如s d 等,但如果您的数据将始终替换为字符串,那么您可以跳过String.format(sb.toString(), valuesList.toArray()) 并将所有键替换为值早一点。

这是一个更简单的版本,它只接受具有 &lt;String,String&gt; 键值关系的映射。

static Pattern stringsPattern = Pattern.compile("%\\(([^)]+)\\)s\\b");

public static String formatStrings(String pattern, Map<String, String> map) 
    StringBuffer sb = new StringBuffer();
    Matcher m = stringsPattern.matcher(pattern);

    while (m.find()) 
        // we can't use null as replacement so we need to convert it to String
        // first. We can do it with String.valueOf method
        m.appendReplacement(sb, String.valueOf(map.get(m.group(1))));
    
    m.appendTail(sb);

    return sb.toString();

【讨论】:

【参考方案3】:

如果您想要更高级的技术,例如 i18n 支持,您可以使用高级消息格式功能

例如: 在语言属性文件中添加属性“模板”,这是您的消息

template = At 2,time,short on 2,date,long, \
    we detected 1,number,integer spaceships on \
    the planet 0.

然后你可以格式化你的变量传递一个数组中的参数:

Object[] messageArguments = 
    "Mars",
    new Integer(7),
    new Date()
;

你这样调用格式化程序:

MessageFormat formatter = new MessageFormat("");
formatter.setLocale(currentLocale);
formatter.applyPattern(messages.getString("template"));
String output = formatter.format(messageArguments);

详细的例子在这里 http://docs.oracle.com/javase/tutorial/i18n/format/messageFormat.html

【讨论】:

【参考方案4】:

你想要String.format方法。

String data  = "Hello, %s";
String updated = String.format(data, "Felix");

【讨论】:

【参考方案5】:

AFAIK 您可以为此使用String#format

String name = "Felix";
String s = String.format("Hello, %s", name);
System.out.println(s);

这将打印出来

Hello, Felix

有关如何使用String#format 格式的更多信息,请访问java.util.Formatter syntax

【讨论】:

以上是关于有没有办法在 Java 中呈现像 'Hello, %(name)s' % 'name':'Felix' 这样的字符串?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法以 HTML/XML 方式呈现 JAVA 组件/容器?

有没有办法像在 javascript 中一样在 C# 中交替使用单引号和双引号?

从 Java 检索网页上动态呈现的 SVG

Hello Blazor:像ASP.NET WebForm一样写代码

当查询被填充时,有没有办法在 Django 模板中呈现大型查询集?

有没有办法在 RealityKit 中将视图呈现为实体?