속도 템플릿과 유사한 자바의 문자열 대체


96

String텍스트로 객체를 전달할 수있는 Java에 대체 메커니즘 이 있습니까? 문자열이 발생하면이를 대체합니다.
예를 들어 텍스트는 다음과 같습니다.

Hello ${user.name},
    Welcome to ${site.name}. 

내가 가진 개체는 "user""site"입니다. 내부 ${}에 주어진 문자열을 객체의 동등한 값 으로 바꾸고 싶습니다 . 이것은 속도 템플릿에서 객체를 교체하는 것과 같습니다.


1
대체 어디? 수업? JSP? 다음과 같은 경우 문자열에 형식 지정 방법이 있습니다.String.format("Hello %s", username);
Droo

1
@Droo : 예제에서 문자열은 like Hello ${user.name}, not like Hello %s또는 Hello {0}.
Adeel Ansari

2
속도처럼 보이고 속도 같은 냄새가 나는 것이 필요하다면 속도일까요? :)
serg

@Droo : 클래스가 아닙니다. 위의 텍스트가 "String"변수에 있고 $ {} 내부의 모든 문자열을 해당 개체의 값으로 바꾸려고합니다. 예를 들어 모든 $ {user.name}을 "user"개체의 이름 속성으로 바꿉니다.

@serg : 네, 속도 코드입니다. 내 코드에서 속도를 제거하고 싶습니다.

답변:


142

StringSubstitutorApache Commons Text에서 사용합니다 .

https://commons.apache.org/proper/commons-text/

그것은 당신을 위해 할 것입니다 (그리고 오픈 소스 ...)

 Map<String, String> valuesMap = new HashMap<String, String>();
 valuesMap.put("animal", "quick brown fox");
 valuesMap.put("target", "lazy dog");
 String templateString = "The ${animal} jumped over the ${target}.";
 StringSubstitutor sub = new StringSubstitutor(valuesMap);
 String resolvedString = sub.replace(templateString);

4

1
이어야 Map<String, String> valuesMap = new HashMap<String, String>();합니다.
andrewrjones 2013-08-21

3
StrSubstitutor이제 https://commons.apache.org/proper/commons-lang/ 에서 더 이상 사용되지 않습니다 . 사용자 https://commons.apache.org/proper/commons-text/ 대신
Lukuluba

7
StrSubstitutor1.3부터 ​​더 이상 사용되지 않으므로 StringSubstitutor대신 사용하십시오. 이 클래스는 2.0에서 제거됩니다. 가져 오기에 대한 Gradle을 의존성 StringSubstitutor이다org.apache.commons:commons-text:1.4
Yurii Rabeshko

조건 기반 대체를 허용합니까?
Gaurav

131

java.text.MessageFormat클래스를 살펴보면 MessageFormat은 개체 집합을 가져 와서 형식을 지정한 다음 형식이 지정된 문자열을 적절한 위치의 패턴에 삽입합니다.

Object[] params = new Object[]{"hello", "!"};
String msg = MessageFormat.format("{0} world {1}", params);

10
감사! 나는 자바가 이런 간단한 일을 할 수있는 굉장한 템플릿 엔진을 사용하지 않고도 이것을 할 수있는 내장 된 방법을 가지고 있어야한다는 것을 알고 있었다!
Joseph Rajeev Motha 2014 년

2
String.format이 할 수있는 모든 것을 할 수있는 것 같습니다.- stackoverflow.com
questions

6
+1. 인식은 수 format또한 걸리는 Object...이 더 간결한 구문 곳 바람직 사용할 수 있도록 변수 인수format("{0} world {1}", "Hello", "!");
davnicwil

MessageFormat기술적 형식이 중요한 출력이 아닌 이름을 딴 디스플레이 메시지에만 안정적으로 사용할 수 있다는 점에 유의해야합니다 . 예를 들어 숫자는 로케일 설정에 따라 형식이 지정되어 기술적 용도로 사용할 수 없습니다.
Marnes

24

내가 선호하는 방법은 String.format() oneliner이고 타사 라이브러리가 필요하지 않기 때문입니다.

String message = String.format("Hello! My name is %s, I'm %s.", name, age); 

예를 들어 다음과 같은 예외 메시지에서 정기적으로 사용합니다.

throw new Exception(String.format("Unable to login with email: %s", email));

힌트 : Varargs를format() 사용 하기 때문에 원하는만큼 변수를 입력 할 수 있습니다.


동일한 인수를 두 번 이상 반복해야 할 때 유용하지 않습니다. 예 : String.format("Hello! My name is %s, I'm %s. Why is my name %s you ask? Well I'm only %s years old so I don't know", name, age, name, age);. 여기에있는 다른 답변은 각 인수를 한 번만 지정해야합니다.
asherbar

2
@asherbar 형식 문자열에서 인수 인덱스 지정자를 사용할 수 있습니다. 예 :String.format("Hello! My name is %1$s, I'm %2$s. Why is my name %1$s you ask? Well I'm only %2$s years old so I don't know", name, age)
jazzpi

@jazzpi 나는 그것을 결코 몰랐다. 감사!
asherbar

20

나는 이것의 작은 테스트 구현을 함께 던졌다. 기본 아이디어는format 형식 문자열, 객체 맵 및 로컬에있는 이름 하고 전달하는 것입니다.

다음의 출력은 다음과 같습니다.

내 개 이름은 fido이고 Jane Doe는 그를 소유합니다.

public class StringFormatter {

    private static final String fieldStart = "\\$\\{";
    private static final String fieldEnd = "\\}";

    private static final String regex = fieldStart + "([^}]+)" + fieldEnd;
    private static final Pattern pattern = Pattern.compile(regex);

    public static String format(String format, Map<String, Object> objects) {
        Matcher m = pattern.matcher(format);
        String result = format;
        while (m.find()) {
            String[] found = m.group(1).split("\\.");
            Object o = objects.get(found[0]);
            Field f = o.getClass().getField(found[1]);
            String newVal = f.get(o).toString();
            result = result.replaceFirst(regex, newVal);
        }
        return result;
    }

    static class Dog {
        public String name;
        public String owner;
        public String gender;
    }

    public static void main(String[] args) {
        Dog d = new Dog();
        d.name = "fido";
        d.owner = "Jane Doe";
        d.gender = "him";
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("d", d);
        System.out.println(
           StringFormatter.format(
                "My dog is named ${d.name}, and ${d.owner} owns ${d.gender}.", 
                map));
    }
}

참고 : 처리되지 않은 예외로 인해 컴파일되지 않습니다. 그러나 코드를 훨씬 쉽게 읽을 수 있습니다.

또한 코드에서 직접 맵을 구성해야한다는 점이 마음에 들지 않지만 프로그래밍 방식으로 지역 변수의 이름을 얻는 방법을 모릅니다. 이를 수행하는 가장 좋은 방법은 객체를 생성하자마자지도에 객체를 배치하는 것입니다.

다음 예제는 예제에서 원하는 결과를 생성합니다.

public static void main(String[] args) {
    Map<String, Object> map = new HashMap<String, Object>();
    Site site = new Site();
    map.put("site", site);
    site.name = "StackOverflow.com";
    User user = new User();
    map.put("user", user);
    user.name = "jjnguy";
    System.out.println(
         format("Hello ${user.name},\n\tWelcome to ${site.name}. ", map));
}

나는 또한 Velocity가 무엇인지 모른다는 것을 언급해야 하므로이 답변이 관련이 있기를 바랍니다.


이것이 제가 찾던 것입니다. 구현해 주셔서 감사합니다. 나는 그것을 시도하고 잘못된 결과를 얻었습니다. :디. 어쨌든 그것은 내 문제를 해결했습니다.

2
@Joe, 도와 드릴 수있어서 다행입니다. Java에서 리플렉션을 사용하는 코드 작성을 마침내 연습하는 것이 저에게 좋은 변명이었습니다.
jjnguy

6

이 작업을 수행하는 방법에 대한 개요가 있습니다. 실제 코드로 구현하는 것은 비교적 간단해야합니다.

  1. 템플릿에서 참조 할 모든 개체의 맵을 만듭니다.
  2. 정규식을 사용하여 템플릿에서 변수 참조를 찾고 해당 값으로 바꿉니다 (3 단계 참조). Matcher를 찾기 및 바꾸기 위해 클래스는 유용합니다.
  3. 점에서 변수 이름을 분할합니다. user.name될 것입니다 username. 조회 user객체를 얻고 사용하도록지도에서 반사 의 값을 얻을 name목적에서입니다. 객체에 표준 getter가 있다고 가정하면 메서드를 getName찾아 호출합니다.

헤, 방금이 대답을 봤습니다. 내 것과 동일합니다. 내 구현에 대해 어떻게 생각하는지 알려주세요.
jjnguy


0

속도는 그 문제를 정확히 해결하기 위해 작성 되었기 때문에 속도에 필적하는 즉시 사용할 수있는 것은 없습니다. 시도 할 수있는 가장 가까운 방법은 Formatter를 살펴 보는 것입니다.

http://cupi2.uniandes.edu.co/site/images/recursos/javadoc/j2se/1.5.0/docs/api/java/util/Formatter.html

그러나 내가 아는 한 포맷터는 Java에서 포맷 옵션과 같은 C를 제공하기 위해 만들어 졌으므로 가려움증을 정확히 긁지 않을 수 있지만 시도해 볼 수 있습니다 :).


0

Java에서 GroovyShell을 사용하여 Groovy GString으로 템플릿을 구문 분석합니다.

Binding binding = new Binding();
GroovyShell gs = new GroovyShell(binding);
// this JSONObject can also be replaced by any Java Object
JSONObject obj = new JSONObject();
obj.put("key", "value");
binding.setProperty("obj", obj)
String str = "${obj.key}";
String exp = String.format("\"%s\".toString()", str);
String res = (String) gs.evaluate(exp);
// value
System.out.println(str);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.