문자열 형식의 명명 된 자리 표시 자
파이썬에서 문자열을 포맷 할 때 자리 표시자를 위치가 아닌 이름으로 채울 수 있습니다.
print "There's an incorrect value '%(value)s' in column # %(column)d" % \
{ 'value': x, 'column': y }
Java에서 가능할지 궁금합니다 (외부 라이브러리없이).
자카르타 커먼즈 랭의 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)");
위의 결과는 다음과 같습니다.
"2 번 열에 잘못된 값 '1'이 있습니다."
Maven을 사용할 때이 종속성을 pom.xml에 추가 할 수 있습니다.
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
확실 하지 않지만 MessageFormat 을 사용 하여 하나의 값을 여러 번 참조 할 수 있습니다 .
MessageFormat.format("There's an incorrect value \"{0}\" in column # {1}", x, y);
위의 내용은 String.format ()으로도 수행 할 수 있지만 복잡한 표현식을 작성 해야하는 경우 messageFormat 구문 클리너를 찾고 문자열에 넣는 객체의 유형에 신경 쓰지 않아도됩니다.
단순한 이름 지정된 자리 표시자를위한 Apache Common StringSubstitutor 의 또 다른 예입니다 .
String template = "Welcome to {theWorld}. My name is {myName}.";
Map<String, String> values = new HashMap<>();
values.put("theWorld", "Stackoverflow");
values.put("myName", "Thanos");
String message = StringSubstitutor.replace(template, values, "{", "}");
System.out.println(message);
// Welcome to Stackoverflow. My name is Thanos.
StringTemplate 라이브러리 를 사용할 수 있으며 원하는 것을 제공합니다.
import org.antlr.stringtemplate.*;
final StringTemplate hello = new StringTemplate("Hello, $name$");
hello.setAttribute("name", "World");
System.out.println(hello.toString());
내용은 매우 간단한 경우 당신은 단순히 하드 코드 된 문자열 교체가 도서관에 대한 필요를 사용할 수 없습니다 :
String url = "There's an incorrect value '%(value)' in column # %(column)";
url = url.replace("%(value)", x); // 1
url = url.replace("%(column)", y); // 2
경고 : 방금 가장 간단한 코드를 보여주고 싶었습니다. 물론 주석에 명시된 바와 같이 보안이 중요한 심각한 생산 코드에는 사용하지 마십시오. 이스케이프, 오류 처리 및 보안이 여기에 있습니다. 그러나 최악의 경우 이제 '좋은'lib를 사용해야하는 이유를 알 수 있습니다 :-)
모든 도움을 주셔서 감사합니다! 모든 단서를 사용하여 원하는 것을 정확하게 수행하는 루틴을 작성했습니다. 사전을 사용하여 파이썬과 같은 문자열 형식. 내가 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());
}
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());
}
예:
String format = "My name is ${1}. ${0} ${1}.";
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.
이것은 오래된 스레드이지만 레코드 용으로 다음과 같이 Java 8 스타일을 사용할 수도 있습니다.
public static String replaceParams(Map<String, String> hashMap, String template) {
return hashMap.entrySet().stream().reduce(template, (s, e) -> s.replace("%(" + e.getKey() + ")", e.getValue()),
(s, s2) -> s);
}
용법:
public static void main(String[] args) {
final HashMap<String, String> hashMap = new HashMap<String, String>() {
{
put("foo", "foo1");
put("bar", "bar1");
put("car", "BMW");
put("truck", "MAN");
}
};
String res = replaceParams(hashMap, "This is '%(foo)' and '%(foo)', but also '%(bar)' '%(bar)' indeed.");
System.out.println(res);
System.out.println(replaceParams(hashMap, "This is '%(car)' and '%(foo)', but also '%(bar)' '%(bar)' indeed."));
System.out.println(replaceParams(hashMap, "This is '%(car)' and '%(truck)', but also '%(foo)' '%(bar)' + '%(truck)' indeed."));
}
출력은 다음과 같습니다.
This is 'foo1' and 'foo1', but also 'bar1' 'bar1' indeed.
This is 'BMW' and 'foo1', but also 'bar1' 'bar1' indeed.
This is 'BMW' and 'MAN', but also 'foo1' 'bar1' + 'MAN' indeed.
나는 당신이 원하는 것을 정확하게 하는 작은 도서관 의 저자입니다 .
Student student = new Student("Andrei", 30, "Male");
String studStr = template("#{id}\tName: #{st.getName}, Age: #{st.getAge}, Gender: #{st.getGender}")
.arg("id", 10)
.arg("st", student)
.format();
System.out.println(studStr);
또는 인수를 연결할 수 있습니다.
String result = template("#{x} + #{y} = #{z}")
.args("x", 5, "y", 10, "z", 15)
.format();
System.out.println(result);
// Output: "5 + 10 = 15"
문자열 도우미 클래스에서 이와 같은 것을 가질 수 있습니다.
/**
* 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();
}
내 대답은 :
a) 가능하면 StringBuilder를 사용하십시오.
b) "자리 표시 자"의 위치 (정수는 달러 매크로와 같은 최고의 특수 문자) 위치를 유지 한 다음 StringBuilder.insert()
(몇 가지 버전의 인수) 사용합니다.
외부 라이브러리를 사용하면 과도하게 보이며 StringBuilder가 내부적으로 String으로 변환 될 때 성능이 크게 저하됩니다.
내가 수업을 만든 답을 바탕으로 MapBuilder
:
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;
}
}
그런 다음 StringFormat
String 형식의 클래스 를 만들었습니다 .
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;
}
}
그런 식으로 사용할 수 있습니다.
String bookingDate = StringFormat.format("From $startDate to $endDate"),
"$startDate", formattedStartDate,
"$endDate", formattedEndDate
);
Apache Commons Lang의 replaceEach 메소드는 특정 요구에 따라 편리 할 수 있습니다. 이 단일 메소드 호출로 이름별로 자리 표시자를 쉽게 대체 할 수 있습니다.
StringUtils.replaceEach("There's an incorrect value '%(value)' in column # %(column)",
new String[] { "%(value)", "%(column)" }, new String[] { x, y });
입력 텍스트가 주어지면 첫 번째 문자열 배열에서 모든 자리 표시자가 두 번째 배열의 해당 값으로 바뀝니다.
나는 변수 발생을 대체하는 문자열을 형식화 할 수있는 util / helper 클래스 (jdk 8 사용)를 만들었습니다.
이를 위해 Matchers "appendReplacement"메소드를 사용하여 모든 대체를 수행하고 형식 문자열의 영향을받는 부분 만 반복합니다.
도우미 클래스는 현재 잘 문서화되어 있지 않습니다. 나는 이것을 나중에 바꿀 것이다;) 어쨌든 나는 가장 중요한 내용을 언급했다.
public class FormatHelper {
//Prefix and suffix for the enclosing variable name in the format string.
//Replace the default values with any you need.
public static final String DEFAULT_PREFIX = "${";
public static final String DEFAULT_SUFFIX = "}";
//Define dynamic function what happens if a key is not found.
//Replace the defualt exception with any "unchecked" exception type you need or any other behavior.
public static final BiFunction<String, String, String> DEFAULT_NO_KEY_FUNCTION =
(fullMatch, variableName) -> {
throw new RuntimeException(String.format("Key: %s for variable %s not found.",
variableName,
fullMatch));
};
private final Pattern variablePattern;
private final Map<String, String> values;
private final BiFunction<String, String, String> noKeyFunction;
private final String prefix;
private final String suffix;
public FormatHelper(Map<String, String> values) {
this(DEFAULT_NO_KEY_FUNCTION, values);
}
public FormatHelper(
BiFunction<String, String, String> noKeyFunction, Map<String, String> values) {
this(DEFAULT_PREFIX, DEFAULT_SUFFIX, noKeyFunction, values);
}
public FormatHelper(String prefix, String suffix, Map<String, String> values) {
this(prefix, suffix, DEFAULT_NO_KEY_FUNCTION, values);
}
public FormatHelper(
String prefix,
String suffix,
BiFunction<String, String, String> noKeyFunction,
Map<String, String> values) {
this.prefix = prefix;
this.suffix = suffix;
this.values = values;
this.noKeyFunction = noKeyFunction;
//Create the Pattern and quote the prefix and suffix so that the regex don't interpret special chars.
//The variable name is a "\w+" in an extra capture group.
variablePattern = Pattern.compile(Pattern.quote(prefix) + "(\\w+)" + Pattern.quote(suffix));
}
public static String format(CharSequence format, Map<String, String> values) {
return new FormatHelper(values).format(format);
}
public static String format(
CharSequence format,
BiFunction<String, String, String> noKeyFunction,
Map<String, String> values) {
return new FormatHelper(noKeyFunction, values).format(format);
}
public static String format(
String prefix, String suffix, CharSequence format, Map<String, String> values) {
return new FormatHelper(prefix, suffix, values).format(format);
}
public static String format(
String prefix,
String suffix,
BiFunction<String, String, String> noKeyFunction,
CharSequence format,
Map<String, String> values) {
return new FormatHelper(prefix, suffix, noKeyFunction, values).format(format);
}
public String format(CharSequence format) {
//Create matcher based on the init pattern for variable names.
Matcher matcher = variablePattern.matcher(format);
//This buffer will hold all parts of the formatted finished string.
StringBuffer formatBuffer = new StringBuffer();
//loop while the matcher finds another variable (prefix -> name <- suffix) match
while (matcher.find()) {
//The root capture group with the full match e.g ${variableName}
String fullMatch = matcher.group();
//The capture group for the variable name resulting from "(\w+)" e.g. variableName
String variableName = matcher.group(1);
//Get the value in our Map so the Key is the used variable name in our "format" string. The associated value will replace the variable.
//If key is missing (absent) call the noKeyFunction with parameters "fullMatch" and "variableName" else return the value.
String value = values.computeIfAbsent(variableName, key -> noKeyFunction.apply(fullMatch, key));
//Escape the Map value because the "appendReplacement" method interprets the $ and \ as special chars.
String escapedValue = Matcher.quoteReplacement(value);
//The "appendReplacement" method replaces the current "full" match (e.g. ${variableName}) with the value from the "values" Map.
//The replaced part of the "format" string is appended to the StringBuffer "formatBuffer".
matcher.appendReplacement(formatBuffer, escapedValue);
}
//The "appendTail" method appends the last part of the "format" String which has no regex match.
//That means if e.g. our "format" string has no matches the whole untouched "format" string is appended to the StringBuffer "formatBuffer".
//Further more the method return the buffer.
return matcher.appendTail(formatBuffer)
.toString();
}
public String getPrefix() {
return prefix;
}
public String getSuffix() {
return suffix;
}
public Map<String, String> getValues() {
return values;
}
}
다음과 같은 값 (또는 접미사 접두사 또는 noKeyFunction)을 사용하여 특정지도에 대한 클래스 인스턴스를 만들 수 있습니다.
Map<String, String> values = new HashMap<>();
values.put("firstName", "Peter");
values.put("lastName", "Parker");
FormatHelper formatHelper = new FormatHelper(values);
formatHelper.format("${firstName} ${lastName} is Spiderman!");
// Result: "Peter Parker is Spiderman!"
// Next format:
formatHelper.format("Does ${firstName} ${lastName} works as photographer?");
//Result: "Does Peter Parker works as photographer?"
Further more you can define what happens if a key in the values Map is missing (works in both ways e.g. wrong variable name in format string or missing key in Map). The default behavior is an thrown unchecked exception (unchecked because I use the default jdk8 Function which cant handle checked exceptions) like:
Map<String, String> map = new HashMap<>();
map.put("firstName", "Peter");
map.put("lastName", "Parker");
FormatHelper formatHelper = new FormatHelper(map);
formatHelper.format("${missingName} ${lastName} is Spiderman!");
//Result: RuntimeException: Key: missingName for variable ${missingName} not found.
You can define a custom behavior in the constructor call like:
Map<String, String> values = new HashMap<>();
values.put("firstName", "Peter");
values.put("lastName", "Parker");
FormatHelper formatHelper = new FormatHelper(fullMatch, variableName) -> variableName.equals("missingName") ? "John": "SOMETHING_WRONG", values);
formatHelper.format("${missingName} ${lastName} is Spiderman!");
// Result: "John Parker is Spiderman!"
or delegate it back to the default no key behavior:
...
FormatHelper formatHelper = new FormatHelper((fullMatch, variableName) -> variableName.equals("missingName") ? "John" :
FormatHelper.DEFAULT_NO_KEY_FUNCTION.apply(fullMatch,
variableName), map);
...
For better handling there are also static method representations like:
Map<String, String> values = new HashMap<>();
values.put("firstName", "Peter");
values.put("lastName", "Parker");
FormatHelper.format("${firstName} ${lastName} is Spiderman!", map);
// Result: "Peter Parker is Spiderman!"
Try Freemarker, templating library.
참고URL : https://stackoverflow.com/questions/2286648/named-placeholders-in-string-formatting
'IT story' 카테고리의 다른 글
PreparedStatement의 SQL을 어떻게 얻을 수 있습니까? (0) | 2020.06.14 |
---|---|
Node.js에서 파일 읽기 (0) | 2020.06.14 |
로컬에서 실행되는 웹 페이지에서 로컬 파일에 대한 링크를 만들려면 어떻게해야합니까? (0) | 2020.06.14 |
이 코드는 왜 버퍼 오버 플로우 공격에 취약합니까? (0) | 2020.06.13 |
Bash 스크립트에서 주석 달기 (0) | 2020.06.13 |