Java 字符串格式中的命名占位符
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2286648/
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
Named placeholders in string formatting
提问by Andy
In Python, when formatting string, I can fill placeholders by name rather than by position, like that:
在 Python 中,格式化字符串时,我可以按名称而不是按位置填充占位符,如下所示:
print "There's an incorrect value '%(value)s' in column # %(column)d" % \
{ 'value': x, 'column': y }
I wonder if that is possible in Java (hopefully, without external libraries)?
我想知道这在 Java 中是否可行(希望没有外部库)?
采纳答案by Andy
Thanks for all your help! Using all your clues, I've written routine to do exactly what I want -- python-like string formatting using dictionary. Since I'm Java newbie, any hints are appreciated.
感谢你的帮助!使用您的所有线索,我已经编写了完全按照我的要求执行的例程——使用字典进行类似 Python 的字符串格式化。由于我是 Java 新手,任何提示都值得赞赏。
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());
}
回答by Boris Pavlovi?
回答by giladbu
not quite, but you can use MessageFormatto reference one value multiple times:
不完全是,但您可以使用MessageFormat多次引用一个值:
MessageFormat.format("There's an incorrect value \"{0}\" in column # {1}", x, y);
The above can be done with String.format() as well, but I find messageFormat syntax cleaner if you need to build complex expressions, plus you dont need to care about the type of the object you are putting into the string
以上也可以用 String.format() 来完成,但如果你需要构建复杂的表达式,我发现 messageFormat 语法更清晰,而且你不需要关心你放入字符串的对象的类型
回答by Lombo
You could have something like this on a string helper class
你可以在字符串助手类上有这样的东西
/**
* 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();
}
回答by Fixpoint
You can use StringTemplatelibrary, it offers what you want and much more.
您可以使用StringTemplate库,它提供您想要的以及更多。
import org.antlr.stringtemplate.*;
final StringTemplate hello = new StringTemplate("Hello, $name$");
hello.setAttribute("name", "World");
System.out.println(hello.toString());
回答by schup
StrSubstitutor of jakarta commons lang is a light weight way of doing this provided your values are already formatted correctly.
雅加达公共语言的 StrSubstitutor 是一种轻量级的方法,前提是您的值已经正确格式化。
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)");
The above results in:
以上结果为:
"There's an incorrect value '1' in column # 2"
“第 2 列中有一个不正确的值‘1’”
When using Maven you can add this dependency to your pom.xml:
使用 Maven 时,您可以将此依赖项添加到 pom.xml:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
回答by kayz1
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());
}
Example:
例子:
String format = "My name is . 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;
}
}
.";
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.
回答by Jacek Cz
My answer is to:
我的回答是:
a) use StringBuilder when possible
a) 尽可能使用 StringBuilder
b) keep (in any form: integer is the best, speciall char like dollar macro etc) position of "placeholder" and then use StringBuilder.insert()
(few versions of arguments).
b)保持(以任何形式:整数是最好的,像美元宏等的特殊字符)“占位符”的位置,然后使用StringBuilder.insert()
(几个版本的参数)。
Using external libraries seems overkill and I belive degrade performance significant, when StringBuilder is converted to String internally.
使用外部库似乎有点矫枉过正,我相信当 StringBuilder 在内部转换为 String 时,性能会显着降低。
回答by Rafal Enden
Based on the answerI created MapBuilder
class:
根据我创建的类的答案MapBuilder
:
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;
}
}
then I created class StringFormat
for String formatting:
然后我StringFormat
为字符串格式创建了类:
String bookingDate = StringFormat.format("From $startDate to $endDate"),
"$startDate", formattedStartDate,
"$endDate", formattedEndDate
);
which you could use like that:
你可以这样使用:
String url = "There's an incorrect value '%(value)' in column # %(column)";
url = url.replace("%(value)", x); // 1
url = url.replace("%(column)", y); // 2
回答by Christophe Roussy
For very simple casesyou can simply use a hardcoded String replace, no need for a library there:
对于非常简单的情况,您可以简单地使用硬编码的字符串替换,无需在那里使用库:
##代码##WARNING: I just wanted to show the simplest code possible. Of course DO NOT use this for serious production code where security matters, as stated in the comments: escaping, error handling and security are an issue here. But in the worst case you now know why using a 'good' lib is required :-)
警告:我只是想展示最简单的代码。当然,不要将它用于安全性很重要的严重生产代码,如评论中所述:转义、错误处理和安全性是这里的一个问题。但在最坏的情况下,您现在知道为什么需要使用“好”的库了 :-)