Cadena multilínea Java

Viniendo de Perl, estoy seguro de que me falta el medio de “documento aquí” para 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 de sum en cada línea, ya que 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. ¿Podría alguien explicar por qué piensan eso? No me parece más preferible. Estoy buscando una solución al hecho de que las cadenas multilíneas 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 mi pregunta aún más, no estoy preocupado por el rendimiento en absoluto. Me preocupan los problemas de mantenimiento y diseño.

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 .

Parece que quieres hacer un literal de líneas múltiples, que no existe en Java.

Tu 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 comenzó con una matriz 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 la nueva línea para su sistema particular, necesita usar System.getProperty("line.separator") , o puede usar %n en String.format .

Otra opción es colocar el recurso en un archivo de texto, y solo leer el contenido de ese archivo. Esto sería preferible para cadenas muy grandes para evitar que tus archivos de clase se hinchen innecesariamente.

En Eclipse, si activa la opción “Escape text when pegando en un literal de cadena” (en Preferences> Java> Editor> Typing) y pega una cadena de líneas múltiples entre comillas, automáticamente agregará " y \n" + para todos tus líneas

 String str = "paste your text here"; 

Este es un hilo antiguo, pero una nueva solución bastante elegante (con un único inconveniente) es utilizar una anotación personalizada.

Compruebe: http://www.adrianwalker.org/2011/12/java-multiline-string.html

Editar: la URL anterior parece estar rota. Un proyecto inspirado en ese trabajo está alojado en GitHub:

https://github.com/benelog/multiline

 public final class MultilineStringUsage { /**    

Hello
Multiline
World

*/ @Multiline private static String html; public static void main(final String[] args) { System.out.println(html); } }

El inconveniente es que debe activar el procesador de anotación correspondiente (provisto).

Y probablemente tenga que configurar Eclipse para no formatear automáticamente sus comentarios de Javadoc.

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

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

Esto es algo que nunca debes usar sin pensar en lo que está haciendo. Pero para scripts únicos lo he usado 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(); } 

String.join

Java 8 agregó un nuevo método estático a java.lang.String que 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!" ); 

Si defines tus 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 comstackrse en una fuente Java. Si terminas colocándolos en la fuente, sugiero poner el + al frente y omitir 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 de formateo:

 final String text = join("\r\n" ,"text" ,"text" ,"text" ); 

Los más se convierten en StringBuilder.append, excepto cuando ambas cadenas son constantes, por lo que el comstackdor puede combinarlas en tiempo de comstackción. Al menos, así es como está en el comstackdor de Sun, y sospecho que la mayoría, si no todos los demás comstackdores, harían lo mismo.

Asi que:

 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 ninguna penalización al dividir los literales de cadena en varias líneas con signos más para facilitar la lectura.

En el 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.

 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); 

Lamentablemente, Java no tiene literales de cadenas multilíneas. O bien debe concatenar literales de cadena (usando + o StringBuilder siendo los dos enfoques más comunes para esto) o leer la cadena desde un archivo separado.

Para literales de cadenas múltiples de líneas múltiples, me inclinaría a utilizar un archivo separado y leerlo usando getResourceAsStream() (un método de la clase Class ). Esto hace que sea más fácil encontrar el archivo ya que no tiene que preocuparse por el directorio actual en comparación con el lugar donde se instaló el código. También facilita el empaquetado, porque puedes almacenar el archivo en tu 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 de “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(); } 

Dado que Java (aún) no admite cadenas de múltiples líneas nativas, la única manera por el momento es piratearlas utilizando una de las técnicas antes mencionadas. 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 ')' 

Ponlo en un archivo llamado javastringify.py y tu cadena en un archivo mystring.txt y ejecútalo de la siguiente manera:

 cat mystring.txt | python javastringify.py 

Luego puede copiar la salida y pegarla en su editor.

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

Puede usar scala-code, que es compatible con java, y permite multiline-Strings encerrado 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 una secuencia de comandos, que toma el texto de un archivo externo y lo envuelve como una cadena multilínea-java como esta:

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

para que pueda cortar y pegar fácilmente en su fuente.

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 …

 /**    

Hello
Multiline
World

*/ @Multiline private static String html;

Entonces, el resultado final es que la variable html contiene la cadena multilínea. Sin comillas, sin ventajas, 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!

  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"); 

Puede concatenar sus adjuntos 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.toStirng(); } 

De cualquier manera, prefiera StringBuilder a la notación más.

Ver Java Stringfier . Convierta su texto en un bloque de bloques de StringBuilder escapándose si es necesario.

Una alternativa que aún no he visto como respuesta es el 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(); 

Además, no se menciona el hecho de que java.io.BufferedWriter tiene un método newLine() .

Si te gusta la guayaba de google tanto como yo, puede darte una representación bastante limpia y una forma agradable y sencilla de no codificar también tus caracteres de nueva línea:

 String out = Joiner.on(newline).join(ImmutableList.of( "line1", "line2", "line3")); 

Una solución bastante eficiente e independiente de la plataforma usaría la propiedad del sistema para 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(); 

JEP 326: Raw String Literals implementará cadenas de varias líneas, por lo que podrá escribir algo como:

 String s = ` text text text `; 

En este momento (mayo de 2018), esto no está incluido en el futuro JDK 11 , así que con suerte estará disponible en JDK 12.

Definir mi cadena en un archivo de propiedades?

Las cadenas de líneas múltiples 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.

Una buena opción.

 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); } } 

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

La única vez que StringBuilder es más eficiente es cuando se utilizan múltiples instrucciones para construir el String.

 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."":()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 mi pregunta aún más, no estoy preocupado por el rendimiento en absoluto. Me preocupan los problemas de mantenimiento y diseño.

Hazlo tan claro y simple como puedas.

Un pequeño truco. Usando esto, inyecto javascritp en una página HTML creada dinámicamente

 StringBuilder builder = new StringBuilder(); public String getString() { return builder.toString(); } private DropdownContent _(String a) { builder.append(a); return this; } public String funct_showhide() { return _("function slidedown_showHide(boxId)"). _("{"). _("if(!slidedown_direction[boxId])slidedown_direction[boxId] = 1;"). _("if(!slideDownInitHeight[boxId])slideDownInitHeight[boxId] = 0;"). _("if(slideDownInitHeight[boxId]==0)slidedown_direction[boxId]=slidedownSpeed; "). _("else slidedown_direction[boxId] = slidedownSpeed*-1;"). _("slidedownContentBox = document.getElementById(boxId);"). _("var subDivs = slidedownContentBox.getElementsByTagName('DIV');"). _("for(var no=0;no 

El último modelo JAVA tiene optimizaciones para + con cadenas constantes, emplea un StringBuffer detrás de las escenas, por lo que no querrás saturar tu código con él.

Apunta a una supervisión de JAVA, que no se asemeja a ANSI C en la concatenación automática de cadenas de comillas dobles con solo espacio en blanco entre ellas, por ejemplo:

 const char usage = "\n" "Usage: xxxx \n" "\n" "Removes your options as designated by the required parameter ,\n" "which must be one of the following strings:\n" " love\n" " sex\n" " drugs\n" " rockandroll\n" "\n" ; 

Me encantaría tener una constante de matriz de caracteres de varias líneas en la que se respeten los avances de línea incrustados, por lo que puedo presentar el bloque sin ningún tipo de desorden, por ejemplo:

 String Query = " SELECT some_column, another column FROM one_table a JOIN another_table b ON a.id = b.id AND a.role_code = b.role_code WHERE a.dept = 'sales' AND b.sales_quote > 1000 Order BY 1, 2 " ; 

Para conseguir esto, uno tiene que vencer a los dioses de JAVA.

Use Properties.loadFromXML(InputStream) . No hay necesidad de libs externos.

Mejor que un código desordenado (ya que la mantenibilidad y el diseño son su preocupación), es preferible no utilizar 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 ubica en la misma carpeta YourClass:

     MEGA LONG MULTILINE   

PD .: Puede usar ... "]]> para una cadena similar a xml.

I suggest using a utility as suggested by ThomasP, and then link that into your build process. An external file is still present to contain the text, but the file is not read at runtime. The workflow is then:

  1. Build a ‘textfile to java code’ utility & check into version control
  2. On each build, run the utility against the resource file to create a revised java source
  3. The Java source contains a header like class TextBlock {... followed by a static string which is auto-generated from the resource file
  4. Build the generated java file with the rest of your code

I see at least one case where it should be avoided to use external files for long strings : if these long string are expected values in an unit-test file, because I think the tests should always be written in a way that they don’t rely on any external resource.