Cadena multilínea Java

516

Viniendo de Perl, estoy seguro de que me falta el medio "aquí documento" de crear una cadena de varias líneas en el código fuente:

$string = <<"EOF"  # create a three-line string
text
text
text
EOF

En Java, tengo que tener comillas engorrosas y signos más en cada línea mientras concateno mi cadena multilínea desde cero.

¿Cuáles son algunas mejores alternativas? ¿Definir mi cadena en un archivo de propiedades?

Editar : dos respuestas dicen que StringBuilder.append () es preferible a la notación más. ¿Alguien podría explicar por qué piensan eso? No me parece más preferible en absoluto. Estoy buscando una forma de evitar el hecho de que las cadenas multilínea no son una construcción de lenguaje de primera clase, lo que significa que definitivamente no quiero reemplazar una construcción de lenguaje de primera clase (concatenación de cadenas con más) con llamadas a métodos.

Editar : Para aclarar más mi pregunta, no me preocupa el rendimiento en absoluto. Me preocupan la mantenibilidad y los problemas de diseño.

skiphoppy
fuente
12
StringBuilder.append () es preferible a más cuando se agrega repetidamente a una cadena porque cada vez que lo hace string1 + string2, está asignando un nuevo objeto de cadena y copiando los caracteres de ambas cadenas de entrada. Si agrega n cadenas juntas, estaría haciendo asignaciones n-1 y aproximadamente (n ^ 2) / 2 copias de caracteres. StringBuilder, por otro lado, copia y reasigna con menos frecuencia (aunque todavía hace ambas cosas cuando excede el tamaño de su búfer interno). Teóricamente, hay casos en los que el compilador podría convertir + para usar StringBuilder pero en la práctica quién sabe.
Laurence Gonsalves
55
Cada vez que salto al depurador, + se convierte en una llamada StringBuilder.append (), en Java 1.5. He tenido colegas que confusamente me dicen que StringBuilder tiene un error ya que depuran el código que no parece llamarlo y terminan allí.
skiphoppy
44
Ver también: stackoverflow.com/questions/782810/…
Michael Myers
61
Tenga en cuenta que un literal de cadena compuesto por "abc \ n" + "def \ n", etc. no utiliza StringBuilder: el compilador los pega y los coloca en el archivo .class como un único literal, igual que con otros tipos de Plegado constante.
araqnid
66
La mayoría de los IDE admiten la entrada de cadenas de varias líneas. es decir. simplemente escriba o pegue lo que desee en una cadena "" y agregará \ n y "+" según sea necesario. por ejemplo, puedo pegar 40 líneas de texto en una Cadena y el IDE lo ordena por usted.
Peter Lawrey

Respuestas:

118

Stephen Colebourne ha creado una propuesta para agregar cadenas de varias líneas en Java 7.

Además, Groovy ya tiene soporte para cadenas de varias líneas .

Paul Morie
fuente
14
El proceso de Project Coin para mejoras en Java incluyó cadenas de líneas múltiples mail.openjdk.java.net/pipermail/coin-dev/2009-February/… . Fue rechazado por Oracle blogs.sun.com/darcy/entry/project_coin_final_five .
JodaStephen
8
¿Algún cambio en 2012?
Ilia G
13
Desafortunadamente, esto no parece haber llegado a la especificación.
namuol
3
El enlace blogs.sun.com está roto, pero creo que el contenido está en blogs.oracle.com/darcy/entry/project_coin_final_five ahora.
Donal Fellows
8
Parece que, a partir de enero de 2018, la comunidad está considerando las cadenas de varias líneas. openjdk.java.net/jeps/326
Shane Gannon
485

Parece que quiere hacer un literal multilínea, que no existe en Java.

Su mejor alternativa serán las cadenas que están +juntas. Algunas otras opciones que la gente ha mencionado (StringBuilder, String.format, String.join) solo serían preferibles si comenzara con una serie de cadenas.

Considera esto:

String s = "It was the best of times, it was the worst of times,\n"
         + "it was the age of wisdom, it was the age of foolishness,\n"
         + "it was the epoch of belief, it was the epoch of incredulity,\n"
         + "it was the season of Light, it was the season of Darkness,\n"
         + "it was the spring of hope, it was the winter of despair,\n"
         + "we had everything before us, we had nothing before us";

Versus StringBuilder:

String s = new StringBuilder()
           .append("It was the best of times, it was the worst of times,\n")
           .append("it was the age of wisdom, it was the age of foolishness,\n")
           .append("it was the epoch of belief, it was the epoch of incredulity,\n")
           .append("it was the season of Light, it was the season of Darkness,\n")
           .append("it was the spring of hope, it was the winter of despair,\n")
           .append("we had everything before us, we had nothing before us")
           .toString();

Versus String.format():

String s = String.format("%s\n%s\n%s\n%s\n%s\n%s"
         , "It was the best of times, it was the worst of times,"
         , "it was the age of wisdom, it was the age of foolishness,"
         , "it was the epoch of belief, it was the epoch of incredulity,"
         , "it was the season of Light, it was the season of Darkness,"
         , "it was the spring of hope, it was the winter of despair,"
         , "we had everything before us, we had nothing before us"
);

Versus Java8 String.join():

String s = String.join("\n"
         , "It was the best of times, it was the worst of times,"
         , "it was the age of wisdom, it was the age of foolishness,"
         , "it was the epoch of belief, it was the epoch of incredulity,"
         , "it was the season of Light, it was the season of Darkness,"
         , "it was the spring of hope, it was the winter of despair,"
         , "we had everything before us, we had nothing before us"
);

Si desea que la nueva línea para su sistema particular, o hay que hacer uso System.lineSeparator(), o puede utilizar %nen String.format.

Otra opción es colocar el recurso en un archivo de texto y simplemente leer el contenido de ese archivo. Esto sería preferible para cadenas muy grandes para evitar hinchar innecesariamente sus archivos de clase.

Kip
fuente
246
Además, la primera versión será concatenada automáticamente por el compilador, ya que todas las cadenas se conocen en tiempo de compilación. Incluso si las cadenas no se conocen en tiempo de compilación, no es más lento que StringBuilder o String.format (). La única razón para evitar la concatenación con + 's es si lo está haciendo en un bucle.
Michael Myers
23
El problema con la String.formatversión es que debe mantener el formato sincronizado con el número de líneas.
Bart van Heukelom
44
String.format no es eficiente en comparación con otros dos ejemplos
cmcginty
10
Esta respuesta es una solución muy inapropiada para la pregunta en cuestión. Tenemos macros SAS de 2000 líneas o grupos de consultas SQL de 200 líneas que deseamos copiar y pegar. Sugerir que usemos "" concat para transformar esos textos multilínea en anexos StringBuffer es ridículo.
Beato Geek
21
@BlessedGeek: la pregunta en cuestión era qué opciones estaban disponibles en el lenguaje Java. No mencionó nada sobre el tipo de datos que entran en la cadena. Si hay una solución mejor, puede publicarla como respuesta. Parece que la solución de Josh Curren sería mejor para su situación. Si está molesto porque el idioma no admite literales de varias líneas, entonces este es el lugar equivocado para quejarse.
Kip
188

En Eclipse, si activa la opción "Escape de texto al pegar en un literal de cadena" (en Preferencias> Java> Editor> Escritura) y pega una cadena de líneas múltiples dentro de comillas, se agregará automáticamente "y \n" +para todas sus líneas.

String str = "paste your text here";
Monir
fuente
15
intelij también hace esto por defecto cuando pegas en "" s
Bob B
¿Generalmente dejas en el \rs que Eclipse pone en Windows?
Noumenon
99

Este es un hilo antiguo, pero una solución nueva y bastante elegante (con solo 4, quizás 3 pequeños inconvenientes) es usar una anotación personalizada.

Verificación: http://www.adrianwalker.org/2011/12/java-multiline-string.html

Un proyecto inspirado en ese trabajo está alojado en GitHub:

https://github.com/benelog/multiline

Ejemplo de código Java:

import org.adrianwalker.multilinestring.Multiline;
...
public final class MultilineStringUsage {

  /**
  <html>
    <head/>
    <body>
      <p>
        Hello<br/>
        Multiline<br/>
        World<br/>
      </p>
    </body>
  </html>
  */
  @Multiline
  private static String html;

  public static void main(final String[] args) {
    System.out.println(html);
  }
}

Los inconvenientes son

  1. que tiene que activar el procesador de anotaciones correspondiente (provisto).
  2. esa variable de cadena no se puede definir como variable local Compruebe el proyecto Literales de cadena sin procesar donde puede definir variables como variables locales
  3. esa Cadena no puede contener otras variables como en Visual Basic .Net con XML literal ( <%= variable %>) :-)
  4. ese literal de cadena está delimitado por el comentario JavaDoc (/ **)

Y probablemente tenga que configurar Eclipse / Intellij-Idea para no reformatear automáticamente sus comentarios Javadoc.

Uno puede encontrar esto extraño (los comentarios Javadoc no están diseñados para incrustar nada más que comentarios), pero como esta falta de cadena multilínea en Java es realmente molesta al final, creo que esta es la peor solución.

SRG
fuente
¿Requiere eso que la clase que usa la cadena multilínea sea final? Además, ¿se requiere alguna configuración al desarrollar y ejecutar código desde Eclipse? La URL de referencia menciona los requisitos de configuración de Maven para el procesamiento de anotaciones. No puedo entender qué podría ser necesario, si hay alguno en Eclipse.
David
La anotación es habitable, pero parece que también hubo una fuerte dependencia de Maven. Esa parte quita gran parte del valor de los heredoc que son para simplificar la gestión de pequeños fragmentos de texto.
javadba
3
Puedes hacer esto completamente en eclipse. El enlace que @SRG publicó arriba lo dirige a este enlace . Si está utilizando eclipse, entonces un minuto de configuración y está funcionando.
Michael Plautz
44
Este es probablemente el mayor hack que he visto. EDITAR: No importa ... vea la respuesta de Bob Albrights.
Llew Vallis
1
Hice una extensión de este proyecto y creé uno nuevo que admite variables locales, eche un vistazo al proyecto
deFreitas
62

Otra opción puede ser almacenar cadenas largas en un archivo externo y leer el archivo en una cadena.

Josh Curren
fuente
13
Exactamente. Grandes cantidades de texto no pertenecen a la fuente Java; utilice un archivo de recursos de un formato apropiado, cargado a través de una llamada a Class.getResource (String).
erickson
55
¡Derecha! Puede usar Locale + ResourceBundle también para cargar fácilmente el texto I18N, y luego la llamada String.format () analizará los "\ n" 's como nuevas líneas :) Ejemplo: String readyStr = String.parse (resourceBundle.getString (" Introducción"));
ATorras
6262
No debería tener que externalizar una cadena solo porque es de varias líneas. ¿Qué sucede si tengo una expresión regular que quiero dividir en varias líneas con comentarios? Se ve feo en Java. La @sintaxis para C # es mucho más limpia.
Jeremy Stein
8
Skiphoppy no quiere molestarse con la sobrecarga de tratar con archivos solo para usar una cadena de longitud de párrafo constante. Uso cadenas multilínea todo el tiempo en C ++, incrustadas en mi código fuente, donde las quiero.
Tim Cooper el
99
Guau. ¡No puedo creer que C ++ sea realmente mejor que Java en este tema! Me encantan las constantes de cadena de varias líneas y, en algunos casos, pertenecen a la fuente.
Usuario1
59

Esto es algo que nunca debes usar sin pensar en lo que está haciendo. Pero para guiones únicos, he usado esto con gran éxito:

Ejemplo:

    System.out.println(S(/*
This is a CRAZY " ' ' " multiline string with all sorts of strange 
   characters!
*/));

Código:

// From: http://blog.efftinge.de/2008/10/multi-line-string-literals-in-java.html
// Takes a comment (/**/) and turns everything inside the comment to a string that is returned from S()
public static String S() {
    StackTraceElement element = new RuntimeException().getStackTrace()[1];
    String name = element.getClassName().replace('.', '/') + ".java";
    StringBuilder sb = new StringBuilder();
    String line = null;
    InputStream in = classLoader.getResourceAsStream(name);
    String s = convertStreamToString(in, element.getLineNumber());
    return s.substring(s.indexOf("/*")+2, s.indexOf("*/"));
}

// From http://www.kodejava.org/examples/266.html
private static String convertStreamToString(InputStream is, int lineNum) {
    /*
     * To convert the InputStream to String we use the BufferedReader.readLine()
     * method. We iterate until the BufferedReader return null which means
     * there's no more data to read. Each line will appended to a StringBuilder
     * and returned as String.
     */
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    StringBuilder sb = new StringBuilder();

    String line = null; int i = 1;
    try {
        while ((line = reader.readLine()) != null) {
            if (i++ >= lineNum) {
                sb.append(line + "\n");
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return sb.toString();
}
Bob Albright
fuente
15
Requiere enviar el código Java para la clase con el binario final. Hmm
Thorbjørn Ravn Andersen
23
Me imagino la reacción de mis compañeros de trabajo cuando trato de comprobar algo como esto en ...
Landon Kuhn
15
+1. Alguna falta de imaginación por parte de las personas que votan en contra. Esta es una construcción útil para escribir pequeñas utilidades, casos de prueba e incluso en entornos de producción controlados. Esta es una diferencia entre abandonar Java en Ruby / Python / etc. o quedarse aquí.
Java
1
Gran solución, pero desafortunadamente no funcionará para Android ya que se ejecutará en un emulador o dispositivo real y no hay código fuente allí.
evgeny.myasishchev
Tal vez sea un problema de Java 8, o algo más, pero el ClassLoader en el ejemplo no existe. Intenté usar MyClass.class.getResourceAsStream (...), pero siempre devuelve nulo. ¡Habría sido una excelente solución rápida para probar!
Nick
54

String.join

Java 8 agregó un nuevo método estático al java.lang.Stringque ofrece una alternativa ligeramente mejor:

String.join( CharSequence delimiter , CharSequence... elements )

Utilizándolo:

String s = String.join(
    System.getProperty("line.separator"),
    "First line.",
    "Second line.",
    "The rest.",
    "And the last!"
);
icza
fuente
55
¡Solución ordenada y limpia! ¡No depende del IDE y del preprocesador! ¡No "\n"se necesita manual , y es consciente de la portabilidad!
Siu Ching Pong -Asuka Kenji-
1
Entiendo que mi comentario es inútil, pero es muy interesante buscar trucos para algo tan básico como un literal de cadena multilínea. ¿Por qué diablos no puede Java todavía agregar esto a la especificación?
dmitry
22

JEP 355: Bloques de texto (Vista previa) tiene como objetivo cubrir esta funcionalidad, actualmente apunta a JDK 13 como una función de vista previa. Permitiendo escribir algo como:

String s = """
    text
    text
    text
  """;

Anterior a este JEP, en JDK12, JEP 326: Raw String Literals tenía como objetivo implementar una característica similar, pero finalmente se retiró.

alostale
fuente
2
Dejaron caer este apoyo
deFreitas
2
Los bloques de texto ahora son parte de Java 13.
ZhekaKozlov
19

Si define sus cadenas en un archivo de propiedades, se verá mucho peor. IIRC, se verá así:

string:text\u000atext\u000atext\u000a

En general, es una idea razonable no incrustar cadenas grandes en la fuente. Es posible que desee cargarlos como recursos, tal vez en XML o en un formato de texto legible. Los archivos de texto pueden leerse en tiempo de ejecución o compilarse en la fuente Java. Si terminas colocándolos en la fuente, sugiero poner el+ el frente y omita nuevas líneas innecesarias:

final String text = ""
    +"text "
    +"text "
    +"text"
;

Si tiene nuevas líneas, es posible que desee un método de combinación o formato:

final String text = join("\r\n"
    ,"text"
    ,"text"
    ,"text"
);
Tom Hawtin - tackline
fuente
17

Las ventajas se convierten en StringBuilder.append, excepto cuando ambas cadenas son constantes para que el compilador pueda combinarlas en el momento de la compilación. Al menos, así es como está en el compilador de Sun, y sospecharía que la mayoría, si no todos los demás compiladores, harían lo mismo.

Entonces:

String a="Hello";
String b="Goodbye";
String c=a+b;

normalmente genera exactamente el mismo código que:

String a="Hello";
String b="Goodbye":
StringBuilder temp=new StringBuilder();
temp.append(a).append(b);
String c=temp.toString();

Por otra parte:

String c="Hello"+"Goodbye";

es lo mismo que:

String c="HelloGoodbye";

Es decir, no hay penalización al dividir los literales de cadena en varias líneas con signos más de legibilidad.

Arrendajo
fuente
44
para ser técnico, en su primer ejemplo genera algo más como: String c = new StringBuilder (). append (a) .append (b) .toString (); La diferencia es que el generador de cadenas temporal está fuera del alcance y es elegible para la recolección de basura inmediatamente después de la línea String c = ..., mientras que el generador de cadenas "temp" se quedaría un poco más.
Kip
Cierto. Mi punto, por supuesto, es distinguir cuándo se llama una función en tiempo de ejecución y cuándo se puede hacer el trabajo en tiempo de compilación. Pero tienes razón.
Jay
15

En IntelliJ IDE solo necesita escribir:

""

Luego coloca el cursor dentro de las comillas y pega la cadena. El IDE lo expandirá en múltiples líneas concatenadas.

nurettin
fuente
11

Lamentablemente, Java no tiene literales de cadena de varias líneas. Tiene que concatenar literales de cadena (usando + o StringBuilder son los dos enfoques más comunes para esto) o leer la cadena desde un archivo separado.

Para los literales de cadena de varias líneas grandes, me inclinaría a usar un archivo separado y leerlo usando getResourceAsStream()(un método deClass clase). Esto facilita la búsqueda del archivo, ya que no tiene que preocuparse por el directorio actual en lugar de donde se instaló el código. También facilita el empaquetado, ya que en realidad puede almacenar el archivo en su archivo jar.

Supongamos que estás en una clase llamada Foo. Solo haz algo como esto:

Reader r = new InputStreamReader(Foo.class.getResourceAsStream("filename"), "UTF-8");
String s = Utils.readAll(r);

La otra molestia es que Java no tiene un método estándar para "leer todo el texto de este lector en una cadena". Sin embargo, es bastante fácil de escribir:

public static String readAll(Reader input) {
    StringBuilder sb = new StringBuilder();
    char[] buffer = new char[4096];
    int charsRead;
    while ((charsRead = input.read(buffer)) >= 0) {
        sb.append(buffer, 0, charsRead);
    }
    input.close();
    return sb.toString();
}
Laurence Gonsalves
fuente
Yo hago lo mismo. Puede usar commons-io para leer más fácilmente el contenido del archivo (con "FileUtils.readFileToString (archivo de archivo)").
SRG
Ya no es cierto acerca de java, no tiene un método estándar de lectura de todo el texto ... - desde Java 7 puede usar Files.readAllLines (Path)
ccpizza
10
String newline = System.getProperty ("line.separator");
string1 + newline + string2 + newline + string3

Pero, la mejor alternativa es usar String.format

String multilineString = String.format("%s\n%s\n%s\n",line1,line2,line3);
Tom
fuente
Mi opinión es que elimina los signos más y las comillas, lo que lo hace más legible, especialmente cuando hay más de 3 líneas. Sin embargo, no es tan bueno como String.format.
Tom
2
El ejemplo de Stringbuilder es al menos tan ilegible. Además, no olvide que "\ n" no siempre es una nueva línea, pero está bien para máquinas Linux y Unix.
Stefan Thyberg
Además, solo quería mencionar la existencia de StringBuilder.
Tom
44
Reemplazar un signo más con un nombre de método de seis caracteres y paréntesis no me parece más legible, aunque aparentemente no eres el único que piensa de esa manera. Sin embargo, no elimina las comillas. Todavía están ahí.
skiphoppy
9

Dado que Java (todavía) no admite nativamente cadenas de varias líneas, la única forma por ahora es piratearlo utilizando una de las técnicas mencionadas anteriormente. Creé el siguiente script de Python usando algunos de los trucos mencionados anteriormente:

import sys
import string
import os

print 'new String('
for line in sys.stdin:
    one = string.replace(line, '"', '\\"').rstrip(os.linesep)
    print '  + "' + one + ' "'
print ')'

Ponga eso en un archivo llamado javastringify.py y su cadena en un archivo mystring.txt y ejecútelo de la siguiente manera:

cat mystring.txt | python javastringify.py

Luego puede copiar el resultado y pegarlo en su editor.

Modifique esto según sea necesario para manejar casos especiales, pero esto funciona para mis necesidades. ¡Espero que esto ayude!

scorpiodawg
fuente
9

Puede usar el código scala, que es compatible con java, y permite cadenas de líneas múltiples encerradas con "" ":

package foobar
object SWrap {
  def bar = """John said: "This is
  a test
  a bloody test,
  my dear." and closed the door.""" 
}

(tenga en cuenta las comillas dentro de la cadena) y de Java:

String s2 = foobar.SWrap.bar ();

Si esto es más cómodo ...?

Otro enfoque, si a menudo maneja texto largo, que debe colocarse en su código fuente, podría ser un script, que toma el texto de un archivo externo y lo envuelve como una cadena-java multilínea como esta:

sed '1s/^/String s = \"/;2,$s/^/\t+ "/;2,$s/$/"/' file > file.java

para que pueda cortarlo y pegarlo fácilmente en su fuente.

usuario desconocido
fuente
8

Puede concatenar sus anexos en un método separado como:

public static String multilineString(String... lines){
   StringBuilder sb = new StringBuilder();
   for(String s : lines){
     sb.append(s);
     sb.append ('\n');
   }
   return sb.toString();
}

De cualquier manera, prefiera StringBuilderla notación más.

usuario54579
fuente
55
¿Por qué prefiero StringBuilder a la notación más?
skiphoppy
10
Eficiencia, o más bien un intento a menudo equivocado.
Michael Myers
2
El intento de eficiencia se basa, creo, en el hecho de que el compilador de Java implementa el operador de concatenación de cadenas usando StringBuilder (StringBuffer en compiladores anteriores a 1.5). Hay un artículo antiguo, pero bien conocido, que establece que hay beneficios de rendimiento en ciertas situaciones al usar StringBuffer (o StringBuilder, ahora). Aquí está el enlace: java.sun.com/developer/JDCTechTips/2002/tt0305.html
Paul Morie
44
Solo cuando el compilador no puede hacerlo. Para literales y constantes, si usa un signo más, la concatenación se realiza en tiempo de compilación. El uso de un StringBuilder obliga a que suceda en tiempo de ejecución, por lo que no solo es más trabajo, sino que es más lento.
johncip
7

En realidad, la siguiente es la implementación más limpia que he visto hasta ahora. Utiliza una anotación para convertir un comentario en una variable de cadena ...

/**
  <html>
    <head/>
    <body>
      <p>
        Hello<br/>
        Multiline<br/>
        World<br/>
      </p>
    </body>
  </html>
  */
  @Multiline
  private static String html;

Entonces, el resultado final es que la variable html contiene la cadena multilínea. Sin comillas, sin más, sin comas, solo cadena pura.

Esta solución está disponible en la siguiente URL ... http://www.adrianwalker.org/2011/12/java-multiline-string.html

¡Espero que ayude!

Rodney P. Barbati
fuente
Ese procesador de anotaciones necesita una verificación más sólida,
Tripp Kinetics
me gusta esto. Ponerlo a prueba
javadba
7

Ver Java Stringfier . Convierte su texto en un bloque Java StringBuilder que se escapa si es necesario.

Leo
fuente
1
Sí, porque puedo pasar mi vida copiando y pegando en ese sitio. También podría almacenarlos en un archivo y cargarlo, pero esa tampoco es una solución ideal.
mmm
7
    import org.apache.commons.lang3.StringUtils;

    String multiline = StringUtils.join(new String[] {
        "It was the best of times, it was the worst of times ", 
        "it was the age of wisdom, it was the age of foolishness",
        "it was the epoch of belief, it was the epoch of incredulity",
        "it was the season of Light, it was the season of Darkness",
        "it was the spring of hope, it was the winter of despair",
        "we had everything before us, we had nothing before us",
        }, "\n");
Mykhaylo Adamovych
fuente
6

Una alternativa que aún no he visto como respuesta es la java.io.PrintWriter.

StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
writer.println("It was the best of times, it was the worst of times");
writer.println("it was the age of wisdom, it was the age of foolishness,");
writer.println("it was the epoch of belief, it was the epoch of incredulity,");
writer.println("it was the season of Light, it was the season of Darkness,");
writer.println("it was the spring of hope, it was the winter of despair,");
writer.println("we had everything before us, we had nothing before us");
String string = stringWriter.toString();

También el hecho de que java.io.BufferedWritertiene un newLine()método no se menciona.

BalusC
fuente
5

Si te gusta la guayaba de Google tanto como a mí, puede darte una representación bastante limpia y una manera agradable y fácil de no codificar tus caracteres de nueva línea también:

String out = Joiner.on(newline).join(ImmutableList.of(
    "line1",
    "line2",
    "line3"));
Dan Lo Bianco
fuente
5

Uso Properties.loadFromXML(InputStream). No hay necesidad de libs externas.

Mejor que un código desordenado (ya que la capacidad de mantenimiento y el diseño son su preocupación), es preferible no usar cadenas largas.

Comience leyendo las propiedades xml:

 InputStream fileIS = YourClass.class.getResourceAsStream("MultiLine.xml");
 Properties prop = new Properies();
 prop.loadFromXML(fileIS);


entonces puedes usar tu cadena multilínea de una manera más fácil de mantener ...

static final String UNIQUE_MEANINGFUL_KEY = "Super Duper UNIQUE Key";
prop.getProperty(UNIQUE_MEANINGFUL_KEY) // "\n    MEGA\n   LONG\n..."


MultiLine.xml` se encuentra en la misma carpeta YourClass:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

<properties>
    <entry key="Super Duper UNIQUE Key">
       MEGA
       LONG
       MULTILINE
    </entry>
</properties>

PD .: Puedes usar <![CDATA["... "]]>para una cadena similar a xml.

jpfreire
fuente
Sí, esto es lo que uso también, ¡gran solución! Mueva el SQL o XML a un archivo de propiedad XML externo. No ensucia el código. :)
Laszlo Lugosi
Esto no responde la pregunta. Los heredoc están por definición dentro del archivo . El punto es mantenerlo en un solo lugar.
javadba
5

Con JDK / 12 acceso anticipado build # 12 , ahora se pueden usar cadenas multilínea en Java de la siguiente manera:

String multiLine = `First line
    Second line with indentation
Third line
and so on...`; // the formatting as desired
System.out.println(multiLine);

y esto da como resultado el siguiente resultado:

First line
    Second line with indentation
Third line
and so on...

Editar: pospuesto a java 13

Naman
fuente
1
Aquí hay una manera de probarlo usando Maven
Naman
2
Como dice Cybersoft en otro comentario, los literales de cadena sin procesar (JEP326) se han eliminado del JDK12 final, pero se ha creado otro JEP para agregar "Bloques de texto" que se pueden hacer como vista previa en JDK 13
Manuel Romeiro
4

Una solución bastante eficiente e independiente de la plataforma sería usar la propiedad del sistema para los separadores de línea y la clase StringBuilder para construir cadenas:

String separator = System.getProperty("line.separator");
String[] lines = {"Line 1", "Line 2" /*, ... */};

StringBuilder builder = new StringBuilder(lines[0]);
for (int i = 1; i < lines.length(); i++) {
    builder.append(separator).append(lines[i]);
}
String multiLine = builder.toString();
Andreas_D
fuente
4

Una buena opcion.

import static some.Util.*;

    public class Java {

        public static void main(String[] args) {

            String sql = $(
              "Select * from java",
              "join some on ",
              "group by"        
            );

            System.out.println(sql);
        }

    }


    public class Util {

        public static String $(String ...sql){
            return String.join(System.getProperty("line.separator"),sql);
        }

    }
Bruno Lee
fuente
4

Esta es una pregunta muy común, así que decidí convertir esta respuesta en un artículo también .

Java 13 y más allá

Las cadenas multilínea ahora son compatibles con Java a través de bloques de texto . En Java 13 y 14, esta característica requiere que establezca la ––enable–previewopción al construir y ejecutar su proyecto. Consulte esta documentación de Java para obtener más detalles.

Ahora, antes de Java 13, así es como escribiría una consulta:

List<Tuple> posts = entityManager
.createNativeQuery(
    "SELECT *\n" +
    "FROM (\n" +
    "    SELECT *,\n" +
    "           dense_rank() OVER (\n" +
    "               ORDER BY \"p.created_on\", \"p.id\"\n" +
    "           ) rank\n" +
    "    FROM (\n" +
    "        SELECT p.id AS \"p.id\",\n" +
    "               p.created_on AS \"p.created_on\",\n" +
    "               p.title AS \"p.title\",\n" +
    "               pc.id as \"pc.id\",\n" +
    "               pc.created_on AS \"pc.created_on\",\n" +
    "               pc.review AS \"pc.review\",\n" +
    "               pc.post_id AS \"pc.post_id\"\n" +
    "        FROM post p\n" +
    "        LEFT JOIN post_comment pc ON p.id = pc.post_id\n" +
    "        WHERE p.title LIKE :titlePattern\n" +
    "        ORDER BY p.created_on\n" +
    "    ) p_pc\n" +
    ") p_pc_r\n" +
    "WHERE p_pc_r.rank <= :rank\n",
    Tuple.class)
.setParameter("titlePattern", "High-Performance Java Persistence %")
.setParameter("rank", 5)
.getResultList();

Gracias a Java 13 Text Blocks, puede volver a escribir esta consulta de la siguiente manera:

List<Tuple> posts = entityManager
.createNativeQuery("""
    SELECT *
    FROM (
        SELECT *,
               dense_rank() OVER (
                   ORDER BY "p.created_on", "p.id"
               ) rank
        FROM (
            SELECT p.id AS "p.id",
                   p.created_on AS "p.created_on",
                   p.title AS "p.title",
                   pc.id as "pc.id",
                   pc.created_on AS "pc.created_on",
                   pc.review AS "pc.review",
                   pc.post_id AS "pc.post_id"
            FROM post p
            LEFT JOIN post_comment pc ON p.id = pc.post_id
            WHERE p.title LIKE :titlePattern
            ORDER BY p.created_on
        ) p_pc
    ) p_pc_r
    WHERE p_pc_r.rank <= :rank
    """,
    Tuple.class)
.setParameter("titlePattern", "High-Performance Java Persistence %")
.setParameter("rank", 5)
.getResultList();

Mucho más legible, ¿verdad?

Soporte IDE

IntelliJ IDEA proporciona soporte para transformar Stringbloques de concatenación heredados al nuevo Stringformato multilínea :

Soporte de bloques de texto IntelliJ IDEA

JSON, HTML, XML

La multilínea Stringes especialmente útil al escribir JSON, HTML o XML.

Considere este ejemplo usando la Stringconcatenación para construir un literal de cadena JSON:

entityManager.persist(
    new Book()
    .setId(1L)
    .setIsbn("978-9730228236")
    .setProperties(
        "{" +
        "   \"title\": \"High-Performance Java Persistence\"," +
        "   \"author\": \"Vlad Mihalcea\"," +
        "   \"publisher\": \"Amazon\"," +
        "   \"price\": 44.99," +
        "   \"reviews\": [" +
        "       {" +
        "           \"reviewer\": \"Cristiano\", " +
        "           \"review\": \"Excellent book to understand Java Persistence\", " +
        "           \"date\": \"2017-11-14\", " +
        "           \"rating\": 5" +
        "       }," +
        "       {" +
        "           \"reviewer\": \"T.W\", " +
        "           \"review\": \"The best JPA ORM book out there\", " +
        "           \"date\": \"2019-01-27\", " +
        "           \"rating\": 5" +
        "       }," +
        "       {" +
        "           \"reviewer\": \"Shaikh\", " +
        "           \"review\": \"The most informative book\", " +
        "           \"date\": \"2016-12-24\", " +
        "           \"rating\": 4" +
        "       }" +
        "   ]" +
        "}"
    )
);

Apenas puede leer el JSON debido a los caracteres que se escapan y la abundancia de comillas dobles y signos más.

Con Java Text Blocks, el objeto JSON se puede escribir así:

entityManager.persist(
    new Book()
    .setId(1L)
    .setIsbn("978-9730228236")
    .setProperties("""
        {
           "title": "High-Performance Java Persistence",
           "author": "Vlad Mihalcea",
           "publisher": "Amazon",
           "price": 44.99,
           "reviews": [
               {
                   "reviewer": "Cristiano",
                   "review": "Excellent book to understand Java Persistence",
                   "date": "2017-11-14",
                   "rating": 5
               },
               {
                   "reviewer": "T.W",
                   "review": "The best JPA ORM book out there",
                   "date": "2019-01-27",
                   "rating": 5
               },
               {
                   "reviewer": "Shaikh",
                   "review": "The most informative book",
                   "date": "2016-12-24",
                   "rating": 4
               }
           ]
        }
        """
    )
);

Desde que usé C # en 2004, he querido tener esta característica en Java, y ahora finalmente la tenemos.

Vlad Mihalcea
fuente
3

¿Definir mi cadena en un archivo de propiedades?

Las cadenas multilínea no están permitidas en los archivos de propiedades. Puede usar \ n en archivos de propiedades, pero no creo que sea una gran solución en su caso.

Dormir
fuente
El valor en un archivo de propiedades puede abarcar varias líneas: simplemente finalice todas las líneas, pero la última con una barra diagonal inversa. Esto deja el problema de lo que usa como separador de línea, ya que es específico de la plataforma. Supongo que podría usar un \ n simple y luego en su código, después de leer la propiedad, haga una búsqueda y reemplazo de \ n a line.separator. Parece un poco burlón, pero supongo que podrías escribir una función que recupere una propiedad y realice esta manipulación al mismo tiempo. Bueno, todo eso supone que estaría escribiendo estas cadenas en un archivo, lo cual es una gran suposición.
Jay
3

Sugiero usar una utilidad como lo sugiere ThomasP, y luego vincularla a su proceso de compilación. Todavía hay un archivo externo para contener el texto, pero el archivo no se lee en tiempo de ejecución. El flujo de trabajo es entonces:

  1. Cree una utilidad 'textfile to java code' e ingrese al control de versiones
  2. En cada compilación, ejecute la utilidad contra el archivo de recursos para crear una fuente Java revisada
  3. La fuente de Java contiene un encabezado como class TextBlock {...seguido de una cadena estática que se genera automáticamente a partir del archivo de recursos
  4. Cree el archivo java generado con el resto de su código
horace
fuente
2

Cuando se usa una larga serie de +, solo se crea un StringBuilder, a menos que el String se determine en el momento de la compilación, en cuyo caso no se usa StringBuilder.

El único momento en que StringBuilder es más eficiente es cuando se utilizan varias instrucciones para construir la cadena.

String a = "a\n";
String b = "b\n";
String c = "c\n";
String d = "d\n";

String abcd = a + b + c + d;
System.out.println(abcd);

String abcd2 = "a\n" +
        "b\n" +
        "c\n" +
        "d\n";
System.out.println(abcd2);

Nota: Solo se crea un StringBuilder.

  Code:
   0:   ldc     #2; //String a\n
   2:   astore_1
   3:   ldc     #3; //String b\n
   5:   astore_2
   6:   ldc     #4; //String c\n
   8:   astore_3
   9:   ldc     #5; //String d\n
   11:  astore  4
   13:  new     #6; //class java/lang/StringBuilder
   16:  dup
   17:  invokespecial   #7; //Method java/lang/StringBuilder."<init>":()V
   20:  aload_1
   21:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   24:  aload_2
   25:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   28:  aload_3
   29:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   32:  aload   4
   34:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   37:  invokevirtual   #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   40:  astore  5
   42:  getstatic       #10; //Field java/lang/System.out:Ljava/io/PrintStream;
   45:  aload   5
   47:  invokevirtual   #11; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   50:  ldc     #12; //String a\nb\nc\nd\n
   52:  astore  6
   54:  getstatic       #10; //Field java/lang/System.out:Ljava/io/PrintStream;
   57:  aload   6
   59:  invokevirtual   #11; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   62:  return

Para aclarar más mi pregunta, no me preocupa el rendimiento en absoluto. Me preocupan la mantenibilidad y los problemas de diseño.

Hazlo lo más claro y simple que puedas.

Peter Lawrey
fuente