Chaîne multiligne Java

Venant de Perl, je manque certainement le moyen “here-document” de créer une chaîne multi-lignes dans le code source:

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

En Java, je dois avoir des guillemets et des signes plus compliqués sur chaque ligne lorsque je concatène ma chaîne multiligne à partir de zéro.

Quelles sont les meilleures alternatives? Définir ma chaîne dans un fichier de propriétés?

Edit : Deux réponses disent que SsortingngBuilder.append () est préférable à la notation plus. Quelqu’un pourrait-il expliquer pourquoi ils le pensent? Cela ne me semble pas préférable du tout. Je cherche un moyen de contourner le fait que les chaînes multilignes ne sont pas une construction de langage de première classe, ce qui signifie que je ne veux certainement pas remplacer une construction de langage de première classe (concaténation de chaînes avec plus) par des appels de méthode.

Edit : Pour clarifier ma question, je ne m’inquiète pas du tout de la performance. Je suis préoccupé par la maintenabilité et les problèmes de conception.

Stephen Colebourne a créé une proposition pour append des chaînes multi-lignes dans Java 7.

De plus, Groovy prend déjà en charge les chaînes multi-lignes .

On dirait que vous voulez faire un littéral multiligne, qui n’existe pas en Java.

Votre meilleure alternative sera les chaînes qui sont juste + ensemble. Certaines autres options mentionnées par les utilisateurs (SsortingngBuilder, Ssortingng.format, Ssortingng.join) ne seraient préférables que si vous aviez démarré avec un tableau de chaînes.

Considère ceci:

 Ssortingng 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 SsortingngBuilder :

 Ssortingng s = new SsortingngBuilder() .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") .toSsortingng(); 

Versus Ssortingng.format() :

 Ssortingng s = Ssortingng.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 Ssortingng.join() :

 Ssortingng s = Ssortingng.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 vous voulez la nouvelle ligne pour votre système particulier, vous devez soit utiliser System.getProperty("line.separator") , soit utiliser %n dans Ssortingng.format .

Une autre option consiste à mettre la ressource dans un fichier texte et à lire le contenu de ce fichier. Cela serait préférable pour les très grandes chaînes afin d’éviter de gonfler inutilement vos fichiers de classe.

Dans Eclipse, si vous activez l’option “Texte d’échappement lors du collage dans un littéral de chaîne” (dans Preferences> Java> Editor> Typing) et collez une chaîne multiligne dans les guillemets, cela appenda automatiquement " et \n" + pour tous vos lignes

 Ssortingng str = "paste your text here"; 

Ceci est un ancien thread, mais une nouvelle solution assez élégante (avec un seul inconvénient) consiste à utiliser une annotation personnalisée.

Check: http://www.adrianwalker.org/2011/12/java-multiline-ssortingng.html

Edit: L’URL ci-dessus semble être cassé. Un projet inspiré de ce travail est hébergé sur GitHub:

https://github.com/benelog/multiline

 public final class MultilineSsortingngUsage { /**    

Hello
Multiline
World

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

L’inconvénient est que vous devez activer le processeur d’annotation correspondant (fourni).

Et vous devez probablement configurer Eclipse pour ne pas reformater automatiquement vos commentaires Javadoc.

On peut trouver cela bizarre (les commentaires Javadoc ne sont pas conçus pour incorporer autre chose que des commentaires), mais comme ce manque de chaîne multiligne en Java est vraiment ennuyant à la fin, je trouve que c’est la pire solution.

Une autre option peut consister à stocker de longues chaînes dans un fichier externe et à lire le fichier dans une chaîne.

C’est quelque chose que vous ne devriez jamais utiliser sans penser à ce qu’il fait. Mais pour les scripts uniques, je l’ai utilisé avec succès:

Exemple:

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

Code:

 // From: http://blog.efftinge.de/2008/10/multi-line-ssortingng-literals-in-java.html // Takes a comment (/**/) and turns everything inside the comment to a ssortingng that is returned from S() public static Ssortingng S() { StackTraceElement element = new RuntimeException().getStackTrace()[1]; Ssortingng name = element.getClassName().replace('.', '/') + ".java"; SsortingngBuilder sb = new SsortingngBuilder(); Ssortingng line = null; InputStream in = classLoader.getResourceAsStream(name); Ssortingng s = convertStreamToSsortingng(in, element.getLineNumber()); return s.subssortingng(s.indexOf("/*")+2, s.indexOf("*/")); } // From http://www.kodejava.org/examples/266.html private static Ssortingng convertStreamToSsortingng(InputStream is, int lineNum) { /* * To convert the InputStream to Ssortingng 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 SsortingngBuilder * and returned as Ssortingng. */ BufferedReader reader = new BufferedReader(new InputStreamReader(is)); SsortingngBuilder sb = new SsortingngBuilder(); Ssortingng 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.toSsortingng(); } 

Ssortingng.join

Java 8 a ajouté une nouvelle méthode statique à java.lang.Ssortingng qui offre une alternative légèrement meilleure:

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

En l’utilisant:

 Ssortingng s = Ssortingng.join( System.getProperty("line.separator"), "First line.", "Second line.", "The rest.", "And the last!" ); 

Si vous définissez vos chaînes dans un fichier de propriétés, cela aura l’air bien pire. IIRC, ça va ressembler à:

 ssortingng:text\u000atext\u000atext\u000a 

En règle générale, il est raisonnable de ne pas intégrer de grandes chaînes à la source. Vous voudrez peut-être les charger en tant que ressources, peut-être au format XML ou lisible. Les fichiers texte peuvent être lus à l’exécution ou compilés dans une source Java. Si vous finissez par les placer dans la source, je suggère de mettre le + à l’avant et d’omettre les nouvelles lignes inutiles:

 final Ssortingng text = "" +"text " +"text " +"text" ; 

Si vous avez de nouvelles lignes, vous voudrez peut-être une méthode de jointure ou de formatage:

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

Les plus sont convertis en SsortingngBuilder.append, sauf lorsque les deux chaînes sont des constantes, de sorte que le compilateur peut les combiner au moment de la compilation. Au moins, c’est comme ça dans le compilateur de Sun, et je soupçonne que la plupart, sinon tous les autres compilateurs feraient la même chose.

Alors:

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

Génère normalement exactement le même code que:

 Ssortingng a="Hello"; Ssortingng b="Goodbye": SsortingngBuilder temp=new SsortingngBuilder(); temp.append(a).append(b); Ssortingng c=temp.toSsortingng(); 

D’autre part:

 Ssortingng c="Hello"+"Goodbye"; 

est le même que:

 Ssortingng c="HelloGoodbye"; 

C’est-à-dire qu’il n’y a pas de pénalité à casser vos littéraux de chaîne sur plusieurs lignes avec des signes plus pour la lisibilité.

Dans l’IntelliJ IDE, il vous suffit de taper:

 "" 

Positionnez ensuite votre curseur à l’intérieur des guillemets et collez votre chaîne. L’EDI va l’étendre en plusieurs lignes concaténées.

 Ssortingng newline = System.getProperty ("line.separator"); ssortingng1 + newline + ssortingng2 + newline + ssortingng3 

Mais, la meilleure alternative est d’utiliser Ssortingng.format

 Ssortingng multilineSsortingng = Ssortingng.format("%s\n%s\n%s\n",line1,line2,line3); 

Malheureusement, Java ne dispose pas de littéraux de chaîne multi-lignes. Vous devez soit concaténer des littéraux de chaîne (en utilisant + ou SsortingngBuilder étant les deux approches les plus courantes) ou lire la chaîne à partir d’un fichier distinct.

Pour les grands littéraux de chaînes multi-lignes, je serais enclin à utiliser un fichier séparé et à le lire en utilisant getResourceAsStream() (une méthode de la classe Class ). Cela facilite la recherche du fichier car vous n’avez pas à vous soucier du répertoire en cours par rapport à l’emplacement d’installation de votre code. Cela facilite également l’emballage, car vous pouvez réellement stocker le fichier dans votre fichier jar.

Supposons que vous soyez dans une classe appelée Foo. Faites juste quelque chose comme ça:

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

La seule autre nuisance est que Java ne dispose pas d’une méthode standard de “lecture de tout le texte de ce lecteur dans une chaîne”. Il est assez facile d’écrire si:

 public static Ssortingng readAll(Reader input) { SsortingngBuilder sb = new SsortingngBuilder(); char[] buffer = new char[4096]; int charsRead; while ((charsRead = input.read(buffer)) >= 0) { sb.append(buffer, 0, charsRead); } input.close(); return sb.toSsortingng(); } 

Puisque Java ne supporte pas (encore) nativement les chaînes multi-lignes, la seule solution pour le moment est de le contourner en utilisant l’une des techniques susmentionnées. J’ai construit le script Python suivant en utilisant certaines des astuces mentionnées ci-dessus:

 import sys import ssortingng import os print 'new Ssortingng(' for line in sys.stdin: one = ssortingng.replace(line, '"', '\\"').rssortingp(os.linesep) print ' + "' + one + ' "' print ')' 

Mettez cela dans un fichier nommé javassortingngify.py et votre chaîne dans un fichier myssortingng.txt et exécutez-le comme suit:

 cat myssortingng.txt | python javassortingngify.py 

Vous pouvez ensuite copier la sortie et la coller dans votre éditeur.

Modifiez-le selon vos besoins pour gérer tous les cas particuliers, mais cela convient à mes besoins. J’espère que cela t’aides!

Vous pouvez utiliser scala-code, compatible avec java, et autoriser les chaînes multilignes entourées de “” “:

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

(notez les guillemets à l’intérieur de la chaîne) et de java:

 Ssortingng s2 = foobar.SWrap.bar (); 

Que ce soit plus confortable …?

Une autre approche, si vous manipulez souvent du texte long, qui devrait être placé dans votre code source, pourrait être un script, qui prend le texte d’un fichier externe et l’enveloppe comme suit:

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

afin que vous puissiez le copier-coller facilement dans votre source.

En fait, ce qui suit est la mise en œuvre la plus propre que j’ai vue jusqu’à présent. Il utilise une annotation pour convertir un commentaire en une variable de chaîne …

 /**    

Hello
Multiline
World

*/ @Multiline private static Ssortingng html;

Ainsi, la variable html contient la chaîne multiligne. Pas de guillemets, pas de plus, pas de virgule, juste de la ficelle pure.

Cette solution est disponible à l’URL suivante … http://www.adrianwalker.org/2011/12/java-multiline-ssortingng.html

J’espère que cela pourra aider!

  import org.apache.commons.lang3.SsortingngUtils; Ssortingng multiline = SsortingngUtils.join(new Ssortingng[] { "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"); 

Vous pouvez concaténer vos ajouts dans une méthode distincte comme:

 public static Ssortingng multilineSsortingng(Ssortingng... lines){ SsortingngBuilder sb = new SsortingngBuilder(); for(Ssortingng s : lines){ sb.append(s); sb.append ('\n'); } return sb.toStirng(); } 

Dans les deux cas, préférez SsortingngBuilder à la notation plus.

Voir Java Ssortingngfier . Transforme votre texte en un bloc Java SsortingngBuilder qui s’échappe si nécessaire.

Une alternative que je n’ai pas encore vue est le java.io.PrintWriter .

 SsortingngWriter ssortingngWriter = new SsortingngWriter(); PrintWriter writer = new PrintWriter(ssortingngWriter); 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"); Ssortingng ssortingng = ssortingngWriter.toSsortingng(); 

De plus, le fait que java.io.BufferedWriter ait une méthode newLine() n’est pas mentionné.

Si vous aimez autant que moi la goyave de Google, cela peut donner une représentation assez nette et un moyen simple et facile de ne pas coder en dur vos caractères de nouvelle ligne:

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

Une solution assez efficace et indépendante de la plate-forme consisterait à utiliser la propriété système pour les séparateurs de lignes et la classe SsortingngBuilder pour créer des chaînes:

 Ssortingng separator = System.getProperty("line.separator"); Ssortingng[] lines = {"Line 1", "Line 2" /*, ... */}; SsortingngBuilder builder = new SsortingngBuilder(lines[0]); for (int i = 1; i < lines.length(); i++) { builder.append(separator).append(lines[i]); } String multiLine = builder.toString(); 

JEP 326: les littéraux de chaîne bruts implémenteront des chaînes à plusieurs lignes, vous pourrez donc écrire quelque chose comme:

 Ssortingng s = ` text text text `; 

En ce moment (mai 2018), ceci n’est pas inclus dans le futur JDK 11 , alors j’espère qu’il sera disponible dans JDK 12.

Définir ma chaîne dans un fichier de propriétés?

Les chaînes multilignes ne sont pas autorisées dans les fichiers de propriétés. Vous pouvez utiliser \ n dans les fichiers de propriétés, mais je ne pense pas que ce soit une bonne solution dans votre cas.

Une bonne option

 import static some.Util.*; public class Java { public static void main(Ssortingng[] args) { Ssortingng sql = $( "Select * from java", "join some on ", "group by" ); System.out.println(sql); } } public class Util { public static Ssortingng $(Ssortingng ...sql){ return Ssortingng.join(System.getProperty("line.separator"),sql); } } 

Lorsqu’une longue série de + est utilisée, un seul SsortingngBuilder est créé, sauf si la chaîne est déterminée au moment de la compilation, auquel cas aucun SsortingngBuilder n’est utilisé!

La seule fois où SsortingngBuilder est plus efficace, c’est lorsque plusieurs instructions sont utilisées pour construire la chaîne.

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

Remarque: un seul SsortingngBuilder est créé.

  Code: 0: ldc #2; //Ssortingng a\n 2: astore_1 3: ldc #3; //Ssortingng b\n 5: astore_2 6: ldc #4; //Ssortingng c\n 8: astore_3 9: ldc #5; //Ssortingng d\n 11: astore 4 13: new #6; //class java/lang/SsortingngBuilder 16: dup 17: invokespecial #7; //Method java/lang/SsortingngBuilder."":()V 20: aload_1 21: invokevirtual #8; //Method java/lang/SsortingngBuilder.append:(Ljava/lang/Ssortingng;)Ljava/lang/SsortingngBuilder; 24: aload_2 25: invokevirtual #8; //Method java/lang/SsortingngBuilder.append:(Ljava/lang/Ssortingng;)Ljava/lang/SsortingngBuilder; 28: aload_3 29: invokevirtual #8; //Method java/lang/SsortingngBuilder.append:(Ljava/lang/Ssortingng;)Ljava/lang/SsortingngBuilder; 32: aload 4 34: invokevirtual #8; //Method java/lang/SsortingngBuilder.append:(Ljava/lang/Ssortingng;)Ljava/lang/SsortingngBuilder; 37: invokevirtual #9; //Method java/lang/SsortingngBuilder.toSsortingng:()Ljava/lang/Ssortingng; 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/Ssortingng;)V 50: ldc #12; //Ssortingng 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/Ssortingng;)V 62: return 

Pour clarifier ma question, je ne m’inquiète pas du tout de la performance. Je suis préoccupé par la maintenabilité et les problèmes de conception.

Rendez-le aussi clair et simple que possible.

Un petit truc En utilisant ceci, j’injecte javascritp dans une page HTML créée dynamicment

 SsortingngBuilder builder = new SsortingngBuilder(); public Ssortingng getSsortingng() { return builder.toSsortingng(); } private DropdownContent _(Ssortingng a) { builder.append(a); return this; } public Ssortingng 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 

JAVA tardif a des optimisations pour + avec des chaînes constantes, utilise un SsortingngBuffer en arrière-plan, vous ne voulez donc pas encombrer votre code avec.

Il indique un oubli JAVA, qui ne ressemble pas à ANSI C dans la concaténation automatique de chaînes entre guillemets doubles avec uniquement des espaces blancs, par exemple:

 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 ssortingngs:\n" " love\n" " sex\n" " drugs\n" " rockandroll\n" "\n" ; 

J’aimerais avoir un tableau de caractères multi-lignes constant où les sauts de ligne intégrés sont respectés, donc je peux présenter le bloc sans encombrement, par exemple:

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

Pour l’obtenir, il faut battre les dieux JAVA.

Utilisez Properties.loadFromXML(InputStream) . Il n’y a pas besoin de librairies externes.

Mieux qu’un code désordonné (puisque la maintenabilité et le design sont votre préoccupation), il est préférable de ne pas utiliser de chaînes longues.

Commencez par lire les propriétés xml:

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

alors vous pouvez utiliser votre chaîne multiligne de manière plus facile à entretenir

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

MultiLine.xml` se trouve dans le même dossier YourClass:

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

PS.: You can use < ![CDATA[" ... "]]> for xml-like ssortingng.

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 ssortingng 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 ssortingngs : if these long ssortingng 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.