Cadeia multilinha Java

516

Vindo do Perl, com certeza estou faltando o meio "aqui-documento" de criar uma string de várias linhas no código-fonte:

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

Em Java, tenho que ter aspas complicadas e sinais de mais em todas as linhas, pois concatenar minha sequência de múltiplas linhas do zero.

Quais são algumas alternativas melhores? Definir minha string em um arquivo de propriedades?

Editar : duas respostas dizem que StringBuilder.append () é preferível à notação positiva. Alguém poderia explicar por que eles pensam assim? Não parece mais preferível para mim. Estou procurando uma maneira de contornar o fato de que cadeias de linhas múltiplas não são uma construção de linguagem de primeira classe, o que significa que definitivamente não quero substituir uma construção de linguagem de primeira classe (concatenação de cadeias com mais) por chamadas de método.

Editar : para esclarecer mais a minha pergunta, não estou preocupado com o desempenho. Estou preocupado com questões de manutenção e design.

skiphoppy
fonte
12
É preferível adicionar StringBuilder.append () ao adicionar repetidamente a uma string, pois toda vez que você faz isso, string1 + string2você está alocando um novo objeto de string e copiando os caracteres de ambas as strings de entrada. Se você estiver adicionando n Strings, estará fazendo alocações n-1 e aproximadamente (n ^ 2) / 2 cópias de caracteres. O StringBuilder, por outro lado, copia e realoca com menos frequência (embora ainda faça as duas coisas quando você excede o tamanho do buffer interno). Teoricamente, há casos em que o compilador pode converter + para usar o StringBuilder, mas na prática quem sabe.
1855 Laurence Gonsalves
5
Toda vez que eu pulo no depurador, o + é convertido em uma chamada StringBuilder.append (), no Java 1.5. Meus colegas me disseram confusamente que o StringBuilder tem um bug, pois eles depuram o código que não parece chamá-lo e acabam lá.
Skiphoppy
4
Veja também: stackoverflow.com/questions/782810/…
Michael Myers
61
Observe que um literal de seqüência de caracteres composto de "abc \ n" + "def \ n" etc. não usa StringBuilder: o compilador os cola e os coloca no arquivo .class como um único literal, igual a outros tipos de dobragem constante.
Araqnid 18/05/2009
6
A maioria dos IDEs suporta a inserção de strings de várias linhas. ie basta digitar ou colar o que deseja em uma "" sequência e ela adicionará \ n e "+" conforme necessário. Por exemplo, eu posso colar 40 linhas de texto em uma String e o IDE classifica isso para você.
Peter Lawrey

Respostas:

118

Stephen Colebourne criou uma proposta para adicionar seqüências de várias linhas no Java 7.

Além disso, o Groovy já tem suporte para seqüências de várias linhas .

Paul Morie
fonte
14
O processo do Project Coin para aprimoramentos em Java incluiu cadeias de linhas múltiplas mail.openjdk.java.net/pipermail/coin-dev/2009-February/… . Foi rejeitado pelos blogs Oracles.sun.com/darcy/entry/project_coin_final_five .
JodaStephen
8
alguma mudança em 2012?
Ilia G
13
Infelizmente, isso não parece ter entrado nas especificações.
Namuol
3
O link blogs.sun.com está quebrado, mas acho que o conteúdo está em blogs.oracle.com/darcy/entry/project_coin_final_five agora.
Bolsistas Donal
8
Parece que, a partir de janeiro de 2018, a comunidade está considerando novamente as seqüências de várias linhas. openjdk.java.net/jeps/326
Shane Gannon
485

Parece que você deseja fazer um literal multilinha, que não existe em Java.

Sua melhor alternativa serão cordas que são apenas + juntas. Algumas outras opções que as pessoas mencionaram (StringBuilder, String.format, String.join) só seriam preferíveis se você começasse com uma matriz de strings.

Considere isto:

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

Se você deseja a nova linha para seu sistema específico, é necessário usar System.lineSeparator() , ou você pode usar %nno String.format.

Outra opção é colocar o recurso em um arquivo de texto e apenas ler o conteúdo desse arquivo. Isso seria preferível para cadeias muito grandes, para evitar inchar desnecessariamente os arquivos de classe.

Kip
fonte
246
Além disso, a primeira versão será concatenada automaticamente pelo compilador, pois todas as strings são conhecidas no momento da compilação. Mesmo que as seqüências de caracteres não sejam conhecidas no momento da compilação, não é mais lento que StringBuilder ou String.format (). A única razão para evitar a concatenação com + é se você estiver fazendo isso em um loop.
Michael Myers
23
O problema com a String.formatversão é que você precisa manter o formato sincronizado com o número de linhas.
Bart van Heukelom
4
String.format não é eficiente em comparação com outros dois exemplos
cmcginty
10
Esta resposta é uma solução muito inadequada para a pergunta em questão. Temos macros SAS de 2000 linhas ou grupos de consultas SQL de 200 linhas que desejamos copiar e colar. Sugerir que usamos + "" concat para transformar esses textos multilinhas em anexos StringBuffer é ridículo.
Blessed Geek
21
@BlessedGeek: a pergunta em questão era sobre quais opções estavam disponíveis na linguagem Java. Não mencionou nada sobre o tipo de dados inseridos na string. Se houver uma solução melhor, você poderá publicá-la como resposta. Parece que a solução de Josh Curren seria melhor para a sua situação. Se você está chateado com o fato de o idioma não suportar literais com várias linhas, então este é o lugar errado para reclamar.
24412 Kip
188

Em Eclipse, se você ativar a opção "text Fuga ao colar em um literal string" (em Preferências> Java> Editor> Typing) e colar uma cadeia multi-alinhada whithin citações, ele irá adicionar automaticamente "e \n" +para todas as suas linhas.

String str = "paste your text here";
Monir
fonte
15
A intelij também faz isso por padrão quando você cola "" s
Bob B
Você geralmente deixa o \rs que o Eclipse coloca no Windows?
Noumenon
99

Este é um encadeamento antigo, mas uma nova solução bastante elegante (com apenas quatro, talvez três pequenas desvantagens) é usar uma anotação personalizada.

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

Um projeto inspirado nesse trabalho está hospedado no GitHub:

https://github.com/benelog/multiline

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

As desvantagens são

  1. que você precisa ativar o processador de anotação correspondente (fornecido).
  2. que a variável String não pode ser definida como variável local Projeto Check Literal String String onde você pode definir variáveis ​​como variáveis ​​locais
  3. essa String não pode conter outras variáveis, como no Visual Basic .Net com literal XML (<%= variable %> ) :-)
  4. esse literal String é delimitado pelo comentário JavaDoc (/ **)

E você provavelmente precisará configurar o Eclipse / Intellij-Idea para não reformatar automaticamente seus comentários do Javadoc.

Pode-se achar estranho (os comentários do Javadoc não são projetados para incorporar nada além de comentários), mas como essa falta de cadeia de linhas multilinhas no Java é realmente irritante no final, acho que essa é a pior solução.

SRG
fonte
Isso exige que a classe que usa a cadeia de linhas múltiplas seja final? Além disso, é necessária alguma configuração ao desenvolver e executar código do Eclipse? O URL de referência menciona os requisitos de configuração do Maven para o processamento de anotações. Não consigo descobrir o que pode ser necessário, se houver no Eclipse.
David
A anotação é habitável - mas parece que também havia uma forte dependência do maven? Essa parte tira muito do valor dos heredoc, que devem simplificar o gerenciamento de pequenos pedaços de texto.
javadba
3
Você pode fazer isso totalmente no eclipse. O link que o @SRG postou acima aponta você para este link . Se você estiver usando o eclipse, um minuto de configuração e ele estará funcionando.
Michael Plautz
4
Este é provavelmente o maior hack que eu já vi. EDIT: Deixa pra lá ... veja a resposta de Bob Albrights.
Llew Vallis
1
Fiz uma extensão deste projeto um criado um novo que variáveis locais são suportados, dê uma olhada no projeto
DeFreitas
62

Outra opção pode ser armazenar seqüências longas em um arquivo externo e ler o arquivo em uma sequência.

Josh Curren
fonte
13
Exatamente. Grandes quantidades de texto não pertencem à fonte Java; use um arquivo de recurso de um formato apropriado, carregado por meio de uma chamada para Class.getResource (String).
22610
5
Direita! Você também pode usar Locale + ResourceBundle para carregar facilmente o texto I18N e a chamada String.format () analisará os "\ n" 's como novas linhas :) Exemplo: String readyStr = String.parse (resourceBundle.getString (" introdução"));
ATorras 18/05/09
62
Você não precisa externalizar uma String apenas porque é multi-linhas. E se eu tiver uma regex que queira dividir em várias linhas com comentários? Parece feio em Java. A @sintaxe para C # é muito mais limpa.
Jeremy Stein
8
Skiphoppy não quer se preocupar com a sobrecarga de lidar com arquivos apenas para usar uma constante de string de comprimento de parágrafo. Eu uso seqüências de caracteres multilinhas o tempo todo em C ++, incorporadas no meu código-fonte, onde eu as quero.
Tim Cooper
9
Uau. Não acredito que C ++ seja realmente melhor que Java nessa questão! Eu amo constantes de seqüência de caracteres de várias linhas e elas pertencem à fonte em alguns casos.
usar o seguinte comando
59

Isso é algo que você nunca deve usar sem pensar no que está fazendo. Mas para scripts únicos, usei isso com grande sucesso:

Exemplo:

    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
fonte
15
Requer o envio do código Java da classe com o binário final. Hmm.
Thorbjørn Ravn Andersen
23
eu posso imaginar a minha reação colegas de trabalho quando eu tento verificar algo assim na ...
Landon Kuhn
15
+1. Alguma falta de imaginação das pessoas que votam para baixo. Essa é uma construção útil para gravar pequenos utilitários, casos de teste e até mesmo em ambientes controlados de produtos. Esse é um diferencial entre deixar o java para o ruby ​​/ python / etc ou ficar aqui.
Javadba
1
Ótima solução, mas infelizmente não funcionará para o Android, pois ele será executado no emulador ou dispositivo real e não há código fonte lá.
evgeny.myasishchev
Talvez seja um prob Java 8, ou qualquer outra coisa, mas o classLoader no exemplo não existe. Tentei usar MyClass.class.getResourceAsStream (...), mas ele sempre retorna nulo. Teria sido uma ótima solução rápida para testes!
Nick
54

String.join

O Java 8 adicionou um novo método estático ao java.lang.Stringqual oferece uma alternativa um pouco melhor:

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

Usando isso:

String s = String.join(
    System.getProperty("line.separator"),
    "First line.",
    "Second line.",
    "The rest.",
    "And the last!"
);
icza
fonte
5
Solução arrumada e limpa! Sem dependência do IDE e do pré-processador! Nenhum manual "\n"é necessário e está ciente da portabilidade!
você precisa saber é o seguinte
1
Eu entendo que meu comentário é inútil, mas é tão reiniciado para procurar hacks para uma coisa tão básica quanto uma literal de cadeia de linhas multilinhas. Por que diabos o java ainda não pode adicionar isso à especificação?
Dmitry
22

O JEP 355: Blocos de Texto (Visualização) visa cobrir essa funcionalidade, atualmente está direcionando o JDK 13 como um recurso de visualização. Permitindo escrever algo como:

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

Antes deste JEP, no JDK12, o JEP 326: literais de cadeia bruta visava implementar um recurso semelhante, mas foi finalmente retirado.

alostale
fonte
2
Deixaram este suporte
deFreitas 23/02/19
2
Blocos de texto agora fazem parte do Java 13.
ZhekaKozlov 16/11/19
19

Se você definir suas strings em um arquivo de propriedades, ficará muito pior. IIRC, será semelhante a:

string:text\u000atext\u000atext\u000a

Geralmente, é uma idéia razoável não incorporar grandes cadeias na origem. Convém carregá-los como recursos, talvez em XML ou em um formato de texto legível. Os arquivos de texto podem ser lidos em tempo de execução ou compilados na fonte Java. Se você acabar colocando-os na fonte, sugiro colocar o item +na frente e omitir novas linhas desnecessárias:

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

Se você possui novas linhas, convém usar o método de junção ou formatação:

final String text = join("\r\n"
    ,"text"
    ,"text"
    ,"text"
);
Tom Hawtin - linha de orientação
fonte
17

As vantagens são convertidas em StringBuilder.append, exceto quando as duas cadeias são constantes, para que o compilador possa combiná-las em tempo de compilação. Pelo menos, é assim no compilador da Sun, e eu suspeitaria mais, se não todos os outros compiladores fariam o mesmo.

Assim:

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

normalmente gera exatamente o mesmo código que:

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

Por outro lado:

String c="Hello"+"Goodbye";

é o mesmo que:

String c="HelloGoodbye";

Ou seja, não há penalidade em quebrar seus literais de string em várias linhas com sinais de adição para facilitar a leitura.

Jay
fonte
4
para ser técnico, em seu primeiro exemplo, gera algo mais como: String c = new StringBuilder (). append (a) .append (b) .toString (); A diferença é que o construtor de strings temporário está fora do escopo e é elegível para coleta de lixo imediatamente após a linha String c = ..., enquanto o construtor de strings "temp" fica um pouco mais.
Kip
Verdade. Meu objetivo, é claro, é distinguir quando uma função é chamada em tempo de execução versus quando o trabalho pode ser feito em tempo de compilação. Mas você está correto.
Jay
15

No IntelliJ IDE, você só precisa digitar:

""

Posicione o cursor entre aspas e cole sua string. O IDE o expandirá em várias linhas concatenadas.

nurettin
fonte
11

Infelizmente, o Java não possui literais de cadeia de várias linhas. Você precisa concatenar literais de sequência (usando + ou StringBuilder sendo as duas abordagens mais comuns para isso) ou ler a sequência em um arquivo separado.

Para literais de grandes cadeias de caracteres de várias linhas, eu estaria inclinado a usar um arquivo separado e a ler usando getResourceAsStream()(um método doClass classe). Isso facilita a localização do arquivo, pois você não precisa se preocupar com o diretório atual e o local em que seu código foi instalado. Também facilita o empacotamento, porque você pode realmente armazenar o arquivo no seu arquivo jar.

Suponha que você esteja em uma classe chamada Foo. Basta fazer algo assim:

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

O outro incômodo é que o Java não possui um método padrão "leia todo o texto deste Reader em uma String". É muito fácil escrever:

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
fonte
Eu faço o mesmo. Você pode usar o commons-io para ler mais facilmente o conteúdo do arquivo (com "FileUtils.readFileToString (arquivo File)").
SRG
Não é verdade anymore sobre java não tem um padrão ler todo método de texto ... - desde Java 7 você pode usar Files.readAllLines (Caminho)
ccpizza
10
String newline = System.getProperty ("line.separator");
string1 + newline + string2 + newline + string3

Mas, a melhor alternativa é usar String.format

String multilineString = String.format("%s\n%s\n%s\n",line1,line2,line3);
Tom
fonte
Minha opinião é que ele remove os sinais de mais e as aspas, tornando-o mais legível, especialmente quando há mais de apenas três linhas. Não é tão bom quanto o String.format.
18249 Tom
2
O exemplo do construtor de string é pelo menos tão ilegível. Além disso, não se esqueça que "\ n" nem sempre é uma nova linha, mas é bom para máquinas linux e unix.
2113 Stefan Thyberg
Além disso, só queria mencionar a existência do StringBuilder.
186 Tom Tom
4
Substituir um sinal de adição por um nome de método de seis caracteres e parênteses não parece mais legível para mim, embora aparentemente você não seja o único que pensa dessa maneira. Porém, não remove as aspas. Eles ainda estão lá.
Skiphoppy
9

Como o Java (ainda) não suporta nativamente seqüências de várias linhas, a única maneira, no momento, é contorná-lo usando uma das técnicas mencionadas acima. Criei o seguinte script Python usando alguns dos truques mencionados acima:

import sys
import string
import os

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

Coloque isso em um arquivo chamado javastringify.py e sua string em um arquivo mystring.txt e execute-o da seguinte maneira:

cat mystring.txt | python javastringify.py

Você pode copiar a saída e colá-la no seu editor.

Modifique isso conforme necessário para lidar com casos especiais, mas isso funciona para minhas necessidades. Espero que isto ajude!

scorpiodawg
fonte
9

Você pode usar o código scala, que é compatível com java, e permite Strings de várias linhas incluídas em "" ":

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

(observe as aspas dentro da string) e de java:

String s2 = foobar.SWrap.bar ();

Se isso é mais confortável ...?

Outra abordagem, se você costuma manipular texto longo, que deve ser colocado em seu código-fonte, pode ser um script, que pega o texto de um arquivo externo e o envolve como uma cadeia de linhas-java como esta:

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

para que você possa recortar e colar facilmente em sua fonte.

usuário desconhecido
fonte
8

Você pode concatenar seus anexos em um 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 qualquer forma, prefira StringBuildera notação de mais.

user54579
fonte
5
Por que prefiro StringBuilder à notação de mais?
Skiphoppy
10
Eficiência, ou melhor, uma tentativa mal orientada.
Michael Myers
2
A tentativa de eficiência baseia-se, eu acho, no fato de que o compilador Java implementa o operador de concatenação de cadeias usando StringBuilder (StringBuffer em compiladores anteriores à 1.5). Há um artigo antigo, mas conhecido, que afirma que há benefícios de desempenho em determinadas situações ao usar o StringBuffer (ou StringBuilder, agora). Aqui está o link: java.sun.com/developer/JDCTechTips/2002/tt0305.html
Paul Morie
4
Somente quando o compilador não puder fazer isso. Para literais e constantes, se você usar um sinal de mais, a concatenação é feita em tempo de compilação. O uso de um StringBuilder obriga a ocorrer em tempo de execução; portanto, não é apenas mais trabalho, é mais lento.
johncip
7

Na verdade, a seguir é a implementação mais limpa que vi até agora. Ele usa uma anotação para converter um comentário em uma variável de string ...

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

Portanto, o resultado final é que a variável html contém a cadeia de linhas múltiplas. Sem aspas, sem vantagens, sem vírgulas, apenas string pura.

Esta solução está disponível no seguinte URL ... http://www.adrianwalker.org/2011/12/java-multiline-string.html

Espero que ajude!

Rodney P. Barbati
fonte
Esse processador de anotação precisa de verificação mais robusto,
Tripp Kinetics
eu gosto disso. Experimentando
javadba 04/04
7

Consulte Java Stringfier . Transforma seu texto em um bloco java StringBuilder, escapando, se necessário.

Leo
fonte
1
Sim, porque posso passar minha vida copiando e colando nesse site. Eu também poderia apenas armazená-los em um arquivo e carregá-lo, mas essa também não é a solução 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
fonte
6

Uma alternativa que ainda não vi como resposta é a 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();

Além disso, o fato de java.io.BufferedWriterter um newLine()método não é mencionado.

BalusC
fonte
5

Se você gosta da goiaba do google tanto quanto eu, ela pode fornecer uma representação bastante limpa e uma maneira fácil e agradável de não codificar também os caracteres da nova linha:

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

Use Properties.loadFromXML(InputStream). Não há necessidade de bibliotecas externas.

Melhor que um código confuso (como a manutenção e o design são sua preocupação), é preferível não usar seqüências longas.

Comece lendo as propriedades xml:

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


então você pode usar sua sequência multilinha de uma maneira mais sustentável ...

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


O MultiLine.xml` fica localizado na mesma pasta 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>

PS .: Você pode usar <![CDATA["... "]]>para string semelhante a xml.

jpfreire
fonte
Sim, é isso que eu uso também, ótima solução! Mova o SQL ou XML para um arquivo de propriedades XML externo. Não estraga o código. :)
Laszlo Lugosi 05/03
Isso não responde à pergunta. heredoc são, por definição, dentro do arquivo . O objetivo é mantê-lo em um só lugar.
Javadba 12/09/16
5

Com o JDK / 12, versão 12 do acesso antecipado , agora é possível usar seqüências de múltiplas linhas em Java da seguinte maneira:

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

e isso resulta na seguinte saída:

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

Edit: Adiado para java 13

Naman
fonte
1
Aqui está uma maneira de testá-lo com o Maven
Naman
2
Como a cybersoft diz em outro comentário, os literais de cadeia de caracteres brutos (JEP326) foram removidos do JDK12 final, mas outro JEP foi criado para adicionar "blocos de texto" que podem ser feitos como visualização no JDK 13
Manuel Romeiro
4

Uma solução bastante eficiente e independente da plataforma seria usar a propriedade do sistema para separadores de linha e a classe StringBuilder para criar cadeias:

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
fonte
4

Uma boa opção.

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
fonte
4

Essa é uma pergunta muito comum, então decidi transformar essa resposta em um artigo também .

Java 13 e além

As cadeias de linhas múltiplas agora são suportadas em Java por meio de blocos de texto . Nos Java 13 e 14, esse recurso requer que você defina a ––enable–previewopção ao criar e executar seu projeto. Confira esta documentação Java para obter mais detalhes.

Agora, antes do Java 13, é assim que você escreveria uma 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();

Graças ao Java 13 Text Blocks, você pode reescrever esta consulta da seguinte maneira:

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

Muito mais legível, certo?

Suporte IDE

O IntelliJ IDEA fornece suporte para transformar Stringblocos de concatenação herdados no novo Stringformato de múltiplas linhas:

Suporte a IntelliJ IDEA Text Blocks

JSON, HTML, XML

A multilinha String é especialmente útil ao escrever JSON, HTML ou XML.

Considere este exemplo usando a Stringconcatenação para criar um literal de string 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" +
        "       }" +
        "   ]" +
        "}"
    )
);

Você mal consegue ler o JSON devido aos caracteres de escape e à abundância de aspas duplas e sinais de adição.

Com os blocos de texto Java, o objeto JSON pode ser escrito assim:

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 usei o C # em 2004, estou querendo ter esse recurso em Java, e agora finalmente o temos.

Vlad Mihalcea
fonte
3

Definir minha string em um arquivo de propriedades?

Cadeias de linhas múltiplas não são permitidas em arquivos de propriedades. Você pode usar \ n nos arquivos de propriedades, mas não acho que seja uma solução muito boa no seu caso.

Kip
fonte
O valor em um arquivo de propriedades pode abranger várias linhas: basta finalizar todas as linhas, exceto a última, com uma barra invertida. Isso deixa o problema do que você usa como separador de linhas, pois isso é específico da plataforma. Suponho que você possa usar um \ n simples e, em seguida, no seu código, depois de ler a propriedade, faça uma pesquisa e substituição de \ n em line.separator. Parece um pouco enganador, mas acho que você pode escrever uma função que recupera uma propriedade e faz essa manipulação ao mesmo tempo. Bem, tudo isso pressupõe que você escreveria essas strings em um arquivo, o que é uma grande suposição.
Jay
3

Sugiro usar um utilitário, conforme sugerido por ThomasP, e depois vincular isso ao seu processo de compilação. Um arquivo externo ainda está presente para conter o texto, mas o arquivo não é lido no tempo de execução. O fluxo de trabalho é então:

  1. Crie um utilitário 'arquivo de texto para código java' e verifique o controle de versão
  2. Em cada construção, execute o utilitário no arquivo de recursos para criar uma fonte java revisada
  3. A fonte Java contém um cabeçalho como class TextBlock {...seguido por uma sequência estática gerada automaticamente a partir do arquivo de recurso
  4. Crie o arquivo java gerado com o restante do seu código
horace
fonte
2

Quando uma série longa de + é usada, apenas um StringBuilder é criado, a menos que o String seja determinado no momento da compilação, caso em que nenhum StringBuilder é usado!

O único momento em que StringBuilder é mais eficiente é quando várias instruções são usadas para construir a 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: Apenas um StringBuilder é criado.

  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 esclarecer melhor minha pergunta, não estou preocupado com o desempenho. Estou preocupado com a manutenção e os problemas de design.

Deixe o mais claro e simples possível.

Peter Lawrey
fonte