Cómo reemplazar un conjunto de fichas en Java Cadena?

Tengo la siguiente Cadena de plantilla: "Hello [Name] Please find attached [Invoice Number] which is due on [Due Date]".

También me han variables de Cadena para el nombre, número de factura y fecha de vencimiento – ¿cuál es la mejor manera de reemplazar las fichas de la plantilla con las variables?

(Tenga en cuenta que si una variable pasa a contener un símbolo NO debe ser sustituido).


EDITAR

Gracias a @laginimaineb y @alan-moore, aquí está mi solución:

public static String replaceTokens(String text, 
                                   Map<String, String> replacements) {
    Pattern pattern = Pattern.compile("\\[(.+?)\\]");
    Matcher matcher = pattern.matcher(text);
    StringBuffer buffer = new StringBuffer();

    while (matcher.find()) {
        String replacement = replacements.get(matcher.group(1));
        if (replacement != null) {
            //matcher.appendReplacement(buffer, replacement);
            //see comment 
            matcher.appendReplacement(buffer, "");
            buffer.append(replacement);
        }
    }
    matcher.appendTail(buffer);
    return buffer.toString();
}
  • Una cosa a tener en cuenta, sin embargo, es que StringBuffer es el mismo que StringBuilder sincronizado. Sin embargo, dado que en este ejemplo no es necesario sincronizar el edificio de la Cadena que sería mejor usar StringBuilder (aunque la adquisición de bloqueos es casi cero costo de operación).
  • Desafortunadamente, usted tiene que utilizar StringBuffer en este caso; es lo que el appendXXX() métodos de esperar. Ellos han estado alrededor desde Java 4, y StringBuilder no se ha agregado hasta Java 5. Como usted dijo, sin embargo, no es gran cosa, sólo una molestia.
  • Una cosa más: appendReplacement(), como la replaceXXX() métodos, busca la captura de las referencias del grupo como $1, $2, etc., y los reemplaza con el texto de la captura asociada grupos. Si su reemplazo de texto puede contener signos de dólar o barras diagonales inversas (que se utilizan para escapar de signos de dólar), usted podría tener un problema. La forma más fácil de lidiar con eso es romper el anexar la operación en dos pasos como yo he hecho en el código anterior.
  • Alan – muy impresionado vio que. Yo no creo que un simple problema sería tan difícil de resolver!
  • el uso de java.sun.com/javase/6/docs/api/java/util/regex/…
  • FYI – Primavera del UriTemplate.expanda el Objeto (…) utiliza exactamente el código de arriba, excepto la revisión añadido por @AlanMoore . static.springsource.org/spring/docs/3.0.x/javadoc-api/org/…
  • Wow, eso es muy, muy fresco. Un limpio, mínimo, completa y eficaz solución. Gracias y’all. Uno de los pocos ejemplos donde regexp hace sentido.
  • Ver java.text.MessageFormat.format("Foo {} {}", "bar", 1);

InformationsquelleAutor Mark | 2009-06-06

15 Kommentare

  1. 65

    La manera más eficiente sería utilizar un comparador para encontrar constantemente las expresiones y reemplazarlos, a continuación, agregar el texto a un generador de cadenas:

    Pattern pattern = Pattern.compile("\\[(.+?)\\]");
    Matcher matcher = pattern.matcher(text);
    HashMap<String,String> replacements = new HashMap<String,String>();
    //populate the replacements map ...
    StringBuilder builder = new StringBuilder();
    int i = 0;
    while (matcher.find()) {
        String replacement = replacements.get(matcher.group(1));
        builder.append(text.substring(i, matcher.start()));
        if (replacement == null)
            builder.append(matcher.group(0));
        else
            builder.append(replacement);
        i = matcher.end();
    }
    builder.append(text.substring(i, text.length()));
    return builder.toString();
    • Esto es lo que yo haría, excepto que me gustaría utilizar el Comparador de appendReplacement() y appendTail() métodos para copiar el inigualable de texto; no hay necesidad de hacerlo a mano.
    • Tienes razón, nunca notado los métodos que existen 😛
    • En realidad el appendReplacement() y appentTail() métodos requieren un StringBuffer, que es snychronized (que no sirve de nada aquí). La respuesta, utiliza un StringBuilder, que es un 20% más rápido en mis pruebas.
  2. 103

    Yo realmente no creo que usted necesita para utilizar un motor de plantillas o algo así para este. Usted puede utilizar el String.formato método, así:

    String template = "Hello %s Please find attached %s which is due on %s";
    
    String message = String.format(template, name, invoiceNumber, dueDate);
    • Una desventaja de esto es que usted tiene que poner los parámetros en el orden correcto
    • Otra es que no se puede especificar su propia reposición de token de formato.
    • otra es que no funciona de forma dinámica, siendo capaz de tener un conjunto de datos de claves/valores y, a continuación, aplicar a cualquier cadena
  3. 42

    Lamentablemente el cómodo método de la Cadena.formato mencionado anteriormente sólo está disponible a partir de Java 1.5 (que debe ser bastante estándar hoy en día, pero nunca se sabe). En lugar de que usted también podría usar Java clase MessageFormat para reemplazar los marcadores de posición.

    Es compatible con marcadores de posición en forma de ‘{número}’, por lo que su mensaje sería como «Hola {0} Sírvase encontrar adjunto {1} que es debido en {2}». Estas Cadenas pueden ser fácilmente exterioriza mediante ResourceBundles (e. g. para la localización con varias configuraciones regionales). La sustitución se haría uso de la estática’format’ método de la clase MessageFormat:

    String msg = "Hello {0} Please find attached {1} which is due on {2}";
    String[] values = {
      "John Doe", "invoice #123", "2009-06-30"
    };
    System.out.println(MessageFormat.format(msg, values));
    • Yo no podía recordar el nombre de MessageFormat, y es un poco tonto ¿cuánto Googleando he tenido que hacer para encontrar esta respuesta. Todo el mundo actúa como String.formato o el uso de una 3ra parte, el olvido de esta increíble herramienta útil.
    • Esto ha estado disponible desde el año 2004 – ¿por qué estoy apenas aprendiendo en el ahora, en el 2017? Estoy de refactorización de código que está cubierto en StringBuilder.append()s y yo estaba pensando: «seguro que hay una mejor manera… algo más Python…» – y a la mierda, creo que este método puede preceder a Python formato métodos. En realidad… esto puede ser mayores de 2002… no puedo encontrar cuando este en realidad vino a la existencia…
  4. 41

    Usted podría tratar de usar una de las plantillas de la biblioteca como Apache Velocity.

    http://velocity.apache.org/

    Aquí está un ejemplo:

    import org.apache.velocity.VelocityContext;
    import org.apache.velocity.app.Velocity;
    
    import java.io.StringWriter;
    
    public class TemplateExample {
        public static void main(String args[]) throws Exception {
            Velocity.init();
    
            VelocityContext context = new VelocityContext();
            context.put("name", "Mark");
            context.put("invoiceNumber", "42123");
            context.put("dueDate", "June 6, 2009");
    
            String template = "Hello $name. Please find attached invoice" +
                              " $invoiceNumber which is due on $dueDate.";
            StringWriter writer = new StringWriter();
            Velocity.evaluate(context, writer, "TemplateName", template);
    
            System.out.println(writer);
        }
    }

    La salida sería:

    Hola Marcos. Sírvase encontrar adjunto factura 42123 que se vence en junio 6, 2009.
    • He usado la velocidad en el pasado. Las grandes obras.
    • de acuerdo, ¿por qué reinventar la rueda
    • Es un poco excesiva para el uso de toda una biblioteca para una tarea tan simple como esto . La velocidad tiene un montón de otras características, y creo firmemente que no es adecuado para una tarea tan simple como esto .
  5. 24

    Puede utilizar la plantilla de la biblioteca para el complejo de la plantilla de reemplazo.

    FreeMarker es una muy buena opción.

    http://freemarker.sourceforge.net/

    Pero para tarea sencilla, no es una simple utilidad de la clase puede ayudar.

    org.apache.commons.lang3.texto.StrSubstitutor

    Es muy potente, personalizable y fácil de usar.

    Esta clase toma un trozo de texto y sustituye todas las variables
    dentro de la misma. La definición predeterminada de una variable es ${nombre de variable}.
    El prefijo y el sufijo puede ser cambiado a través de los constructores y los métodos set.

    Valores de la Variable se suelen resolver a partir de un mapa, pero también podría ser
    resuelto a partir de las propiedades del sistema, o mediante el suministro de una variable personalizada
    resolución.

    Por ejemplo, si desea sustituir la variable de entorno de sistema en una cadena de plantilla,
    aquí está el código:

    public class SysEnvSubstitutor {
        public static final String replace(final String source) {
            StrSubstitutor strSubstitutor = new StrSubstitutor(
                    new StrLookup<Object>() {
                        @Override
                        public String lookup(final String key) {
                            return System.getenv(key);
                        }
                    });
            return strSubstitutor.replace(source);
        }
    }
    • org.apache.commons.lang3.texto.StrSubstitutor trabajado muy bien para mí
  6. 16
    System.out.println(MessageFormat.format("Hello {0}! You have {1} messages", "Join",10L));

    De salida:
    Hola Unirse! Usted tiene 10 mensajes»

    • Juan claramente comprueba sus mensajes tan a menudo como puedo comprobar mi «spam» dado que es un Largo.
  7. 9
    String.format("Hello %s Please find attached %s which is due on %s", name, invoice, date)
    • Gracias – pero en mi caso la cadena de plantilla puede ser modificado por el usuario, por lo que no puedo estar seguro de la orden de los tokens
  8. 8

    Depende de donde los datos reales que desea cambiar se encuentra. Usted podría tener un Mapa como este:

    Map<String, String> values = new HashMap<String, String>();

    que contiene todos los datos que pueden ser reemplazados. A continuación, se puede iterar sobre el mapa y cambiar todo en la Cadena de la siguiente manera:

    String s = "Your String with [Fields]";
    for (Map.Entry<String, String> e : values.entrySet()) {
      s = s.replaceAll("\\[" + e.getKey() + "\\]", e.getValue());
    }

    También puede iterar a través de la Cadena y encontrar los elementos en el mapa. Pero que es un poco más complicado porque se necesita analizar la Cadena de búsqueda para el []. Podría hacerlo con una expresión regular utilizando el Patrón y el Comparador.

  9. 3

    Mi solución para la sustitución de ${variable} estilo de tokens (inspirado por las respuestas y por la Primavera UriTemplate):

    public static String substituteVariables(String template, Map<String, String> variables) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}");
        Matcher matcher = pattern.matcher(template);
        //StringBuilder cannot be used here because Matcher expects StringBuffer
        StringBuffer buffer = new StringBuffer();
        while (matcher.find()) {
            if (variables.containsKey(matcher.group(1))) {
                String replacement = variables.get(matcher.group(1));
                //quote to work properly with $ and {,} signs
                matcher.appendReplacement(buffer, replacement != null ? Matcher.quoteReplacement(replacement) : "null");
            }
        }
        matcher.appendTail(buffer);
        return buffer.toString();
    }
  10. 0

    En el pasado, he resuelto este tipo de problemas con StringTemplate y Groovy Plantillas.

    En última instancia, la decisión de usar un motor de plantillas o no debe estar basado en los siguientes factores:

    • Va a tener muchas de estas plantillas en la aplicación?
    • ¿Necesita la capacidad de modificar las plantillas sin necesidad de reiniciar la aplicación?
    • Que será el mantenimiento de estas plantillas? Un programador de Java o un analista de negocios involucrados en el proyecto?
    • Se necesita la capacidad de aplicar la lógica en sus plantillas, como texto condicional basado en valores en las variables?
    • Se necesita la capacidad de incluir otras plantillas en una plantilla?

    Si alguno de los de arriba se aplica a su proyecto, me gustaría considerar el uso de un motor de plantillas, la mayoría de los cuales disponen de esta funcionalidad, y más.

  11. 0

    He utilizado

    String template = "Hello %s Please find attached %s which is due on %s";
    
    String message = String.format(template, name, invoiceNumber, dueDate);
    • Que iba a funcionar, pero en mi caso la cadena de plantilla personalizable por el usuario, así que no sé en qué orden las fichas aparecerán.
  12. 0

    Con Apache Commons Biblioteca, usted puede simplemente utilizar Stringutils.replaceEach:

    public static String replaceEach(String text,
                                 String[] searchList,
                                 String[] replacementList)

    De la documentación:

    Reemplaza todas las ocurrencias de Cadenas de texto dentro de otra Cadena.

    Una referencia nula pasa a este método, es un no-op, o si cualquier «buscar
    cadena» o «cadena a reemplazar» es nulo, que reemplace será ignorado.
    Esto no se repita. Para repetir reemplaza, llame a la sobrecarga
    método.

     StringUtils.replaceEach(null, *, *)        = null
    
      StringUtils.replaceEach("", *, *)          = ""
    
      StringUtils.replaceEach("aba", null, null) = "aba"
    
      StringUtils.replaceEach("aba", new String[0], null) = "aba"
    
      StringUtils.replaceEach("aba", null, new String[0]) = "aba"
    
      StringUtils.replaceEach("aba", new String[]{"a"}, null)  = "aba"
    
      StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"
    
      StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"
    
      StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
      (example of how it does not repeat)
    
    StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"
  13. 0

    La siguiente sustituye a las variables de la forma <<VAR>>, con valores levantó la vista de un Mapa. Usted puede prueba en línea aquí

    Por ejemplo, con la siguiente cadena de entrada

    BMI=(<<Weight>>/(<<Height>>*<<Height>>)) * 70
    Hi there <<Weight>> was here

    y los siguientes valores de la variable

    Weight, 42
    Height, HEIGHT 51

    salidas de la siguiente

    BMI=(42/(HEIGHT 51*HEIGHT 51)) * 70
    
    Hi there 42 was here

    Aquí está el código

      static Pattern pattern = Pattern.compile("<<([a-z][a-z0-9]*)>>", Pattern.CASE_INSENSITIVE);
    
      public static String replaceVarsWithValues(String message, Map<String,String> varValues) {
        try {
          StringBuffer newStr = new StringBuffer(message);
          int lenDiff = 0;
          Matcher m = pattern.matcher(message);
          while (m.find()) {
            String fullText = m.group(0);
            String keyName = m.group(1);
            String newValue = varValues.get(keyName)+"";
            String replacementText = newValue;
            newStr = newStr.replace(m.start() - lenDiff, m.end() - lenDiff, replacementText);
            lenDiff += fullText.length() - replacementText.length();
          }
          return newStr.toString();
        } catch (Exception e) {
          return message;
        }
      }
    
    
      public static void main(String args[]) throws Exception {
          String testString = "BMI=(<<Weight>>/(<<Height>>*<<Height>>)) * 70\n\nHi there <<Weight>> was here";
          HashMap<String,String> values = new HashMap<>();
          values.put("Weight", "42");
          values.put("Height", "HEIGHT 51");
          System.out.println(replaceVarsWithValues(testString, values));
      }

    y aunque no es necesario, puede utilizar un enfoque similar para sustituir las variables en una cadena con las propiedades de la aplicación.archivo de propiedades, aunque esto ya se está haciendo:

    private static Pattern patternMatchForProperties =
          Pattern.compile("[$][{]([.a-z0-9_]*)[}]", Pattern.CASE_INSENSITIVE);
    
    protected String replaceVarsWithProperties(String message) {
        try {
          StringBuffer newStr = new StringBuffer(message);
          int lenDiff = 0;
          Matcher m = patternMatchForProperties.matcher(message);
          while (m.find()) {
            String fullText = m.group(0);
            String keyName = m.group(1);
            String newValue = System.getProperty(keyName);
            String replacementText = newValue;
            newStr = newStr.replace(m.start() - lenDiff, m.end() - lenDiff, replacementText);
            lenDiff += fullText.length() - replacementText.length();
          }
          return newStr.toString();
        } catch (Exception e) {
          return message;
        }
      }

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea