문자열 형식의 명명된 자리 표시자
Python에서는 문자열을 포맷할 때 다음과 같이 위치별이 아닌 이름으로 플레이스 홀더를 채울 수 있습니다.
print "There's an incorrect value '%(value)s' in column # %(column)d" % \
{ 'value': x, 'column': y }
자바(기대적으로 외부 라이브러리 없이)에서도 가능합니까?
jakarta commons lang의 Str Substituter는 값이 이미 올바르게 포맷되어 있는 경우 이를 위한 가벼운 방법입니다.
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 String Substitutor의 또 다른 예제입니다.
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());
도와주셔서 감사합니다!당신의 모든 단서를 사용하여, 저는 제가 원하는 것을 정확히 하기 위한 루틴을 작성했습니다. 사전을 사용한 비단뱀과 같은 문자열 형식입니다.저는 자바 신인이기 때문에 힌트를 주시면 감사하겠습니다.
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"
Apache Commons를 사용할 수 있습니다.주의:StrSubstitutor
을 사용하다
import org.apache.commons.text.StringSubstitutor;
// ...
Map<String, String> values = new HashMap<>();
values.put("animal", "quick brown fox");
values.put("target", "lazy dog");
StringSubstitutor sub = new StringSubstitutor(values);
String result = sub.replace("The ${animal} jumped over the ${target}.");
// "The quick brown fox jumped over the lazy dog."
이 클래스는 변수에 대한 기본값 제공을 지원합니다.
String result = sub.replace("The number is ${undefined.property:-42}.");
// "The number is 42."
치환을 하려면 , 「재귀 변수 치환」, 「재귀 변수 치환」을합니다.setEnableSubstitutionInVariables(true);
.
Map<String, String> values = new HashMap<>();
values.put("b", "c");
values.put("ac", "Test");
StringSubstitutor sub = new StringSubstitutor(values);
sub.setEnableSubstitutionInVariables(true);
String result = sub.replace("${a${b}}");
// "Test"
Apache Commons Lang의 대체 각 방법은 고객의 특정 요구에 따라 편리할 수 있습니다.이것을 사용하면, 다음의 단일 메서드 호출로 플레이스 홀더를 이름으로 간단하게 치환할 수 있습니다.
StringUtils.replaceEach("There's an incorrect value '%(value)' in column # %(column)",
new String[] { "%(value)", "%(column)" }, new String[] { x, y });
일부 입력 텍스트가 지정되면 첫 번째 문자열 배열의 자리 표시자가 두 번째 문자열의 해당 값으로 모두 바뀝니다.
이 글을 쓰는 시점에서는 자바에 내장된 것은 없습니다.저는 당신이 직접 구현한 것을 제안합니다.저는 맵을 생성하여 기능에 전달하는 대신 단순하고 유창한 빌더 인터페이스를 선호합니다.그 결과 다음과 같이 연속적인 코드 청크가 생성됩니다.
String result = new TemplatedStringBuilder("My name is {{name}} and I from {{town}}")
.replace("name", "John Doe")
.replace("town", "Sydney")
.finish();
간단한 실장은 다음과 같습니다.
class TemplatedStringBuilder {
private final static String TEMPLATE_START_TOKEN = "{{";
private final static String TEMPLATE_CLOSE_TOKEN = "}}";
private final String template;
private final Map<String, String> parameters = new HashMap<>();
public TemplatedStringBuilder(String template) {
if (template == null) throw new NullPointerException();
this.template = template;
}
public TemplatedStringBuilder replace(String key, String value){
parameters.put(key, value);
return this;
}
public String finish(){
StringBuilder result = new StringBuilder();
int startIndex = 0;
while (startIndex < template.length()){
int openIndex = template.indexOf(TEMPLATE_START_TOKEN, startIndex);
if (openIndex < 0){
result.append(template.substring(startIndex));
break;
}
int closeIndex = template.indexOf(TEMPLATE_CLOSE_TOKEN, openIndex);
if(closeIndex < 0){
result.append(template.substring(startIndex));
break;
}
String key = template.substring(openIndex + TEMPLATE_START_TOKEN.length(), closeIndex);
if (!parameters.containsKey(key)) throw new RuntimeException("missing value for key: " + key);
result.append(template.substring(startIndex, openIndex));
result.append(parameters.get(key));
startIndex = closeIndex + TEMPLATE_CLOSE_TOKEN.length();
}
return result.toString();
}
}
ICU4J 공식 도서관을 둘러보셔야 합니다.JDK에서 사용할 수 있는 것과 유사한 MessageFormat 클래스를 제공하지만 이 클래스는 명명된 자리 표시자를 지원합니다.
이 페이지에 기재되어 있는 다른 솔루션과는 다릅니다.ICU4j는 IBM이 관리하고 정기적으로 업데이트하는 ICU 프로젝트의 일부입니다.또, 다원화 등 고도의 사용 사례도 서포트하고 있습니다.
다음은 코드 예시입니다.
MessageFormat messageFormat =
new MessageFormat("Publication written by {author}.");
Map<String, String> args = Map.of("author", "John Doe");
System.out.println(messageFormat.format(args));
스트링 도우미 클래스에서 이런 것을 사용할 수 있습니다.
/**
* 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();
}
내가 만든 답을 바탕으로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
다음 중 하나:
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
);
문자열 형식을 지정할 수 있는 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의 클래스 인스턴스를 만들 수 있습니다.
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?"
또한 값 Map에 키가 없는 경우(포맷 문자열에 잘못된 변수 이름 또는 Map에 누락된 키 등 두 가지 방식으로 모두 작동함) 어떻게 되는지 정의할 수 있습니다.기본 동작은 다음과 같은 체크되지 않은 예외가 느려집니다(체크된 예외를 처리할 수 없는 기본 jdk8 함수를 사용하기 때문에 체크되지 않음).
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.
컨스트럭터 콜에서는 다음과 같은 커스텀 동작을 정의할 수 있습니다.
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!"
또는 기본 no key 동작으로 되돌립니다.
...
FormatHelper formatHelper = new FormatHelper((fullMatch, variableName) -> variableName.equals("missingName") ? "John" :
FormatHelper.DEFAULT_NO_KEY_FUNCTION.apply(fullMatch,
variableName), map);
...
보다 나은 처리를 위해 다음과 같은 정적 메서드 표현도 있습니다.
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!"
자바(Kotlin, JavaScript 등)에서 문자열 보간을 사용하는 자바 플러그인이 있습니다.Java 8, 9, 10, 11 지원...https://github.com/antkorwin/better-strings
문자열 리터럴 변수 사용:
int a = 3;
int b = 4;
System.out.println("${a} + ${b} = ${a+b}");
식 사용:
int a = 3;
int b = 4;
System.out.println("pow = ${a * a}");
System.out.println("flag = ${a > b ? true : false}");
기능 사용:
@Test
void functionCall() {
System.out.println("fact(5) = ${factorial(5)}");
}
long factorial(int n) {
long fact = 1;
for (int i = 2; i <= n; i++) {
fact = fact * i;
}
return fact;
}
자세한 내용은 프로젝트 README을 참조하십시오.
답변은 다음과 같습니다.
a) 가능한 경우 String Builder를 사용합니다.
"홀더"의위치를 유지 등 후 b) "플레이스 홀더"를 사용합니다(즉, "char like dollar", "char like dollar", "char like dollar", "char like dollar"StringBuilder.insert()
(인수의 버전이 표시됩니다).
String Builder가 내부적으로 String으로 변환되면 외부 라이브러리를 사용하면 과잉으로 간주되어 성능이 크게 저하된다고 생각합니다.
프리마커, 템플리트에 가보세요.
나는 단지 빠른 방법으로 시도했다.
public static void main(String[] args)
{
String rowString = "replace the value ${var1} with ${var2}";
Map<String,String> mappedValues = new HashMap<>();
mappedValues.put("var1", "Value 1");
mappedValues.put("var2", "Value 2");
System.out.println(replaceOccurence(rowString, mappedValues));
}
private static String replaceOccurence(String baseStr ,Map<String,String> mappedValues)
{
for(String key :mappedValues.keySet())
{
baseStr = baseStr.replace("${"+key+"}", mappedValues.get(key));
}
return baseStr;
}
유감스럽게도, 빠른 대답은 '아니오'입니다.단, 다음과 같이 합리적인 동기화에 근접할 수 있습니다.
"""
You are $compliment!
"""
.replace('$compliment', 'awesome');
가독성과 예측성이 뛰어납니다.String.format
적어도!
https://dzone.com/articles/java-string-format-examples 문자열format(inputString, [listOfParams])이 가장 쉬운 방법입니다.문자열의 자리 표시자는 순서로 정의할 수 있습니다.자세한 내용은 제공된 링크를 참조하십시오.
언급URL : https://stackoverflow.com/questions/2286648/named-placeholders-in-string-formatting
'programing' 카테고리의 다른 글
MySQL에서 정수 시퀀스 생성 (0) | 2023.02.03 |
---|---|
제출 시 페이지 새로 고침 중지 (0) | 2023.02.03 |
jQuery 플러그인 확인 - 간단한 사용자 지정 규칙을 만드는 방법 (0) | 2023.02.03 |
Python 요청 라이브러리의 get 메서드와 함께 헤더 사용 (0) | 2023.02.03 |
SQL을 사용하여 날짜별로 정렬할 수 있지만 결과 집합의 뒷부분에 늘 날짜를 넣을 수 있습니까? (0) | 2023.02.03 |