Ciąg wielowierszowy Java

516

Pochodząc z Perla, na pewno brakuje mi środka „tutaj-dokument” do tworzenia ciągu wieloliniowego w kodzie źródłowym:

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

W Javie muszę mieć nieporęczne cytaty i znaki plus w każdym wierszu, kiedy odkładam mój ciąg multilinii od zera.

Jakie są lepsze alternatywy? Zdefiniować mój ciąg w pliku właściwości?

Edycja : Dwie odpowiedzi mówią, że StringBuilder.append () jest lepsza niż notacja plus. Czy ktoś mógłby wyjaśnić, dlaczego tak uważa? Nie wygląda mi to wcale lepiej. Szukam sposobu na obejście faktu, że ciągi wielowierszowe nie są konstrukcją języka pierwszej klasy, co oznacza, że ​​zdecydowanie nie chcę zastępować konstrukcji języka pierwszej klasy (konkatenacji ciągów z plusem) wywołaniami metod.

Edycja : Aby wyjaśnić moje pytanie, w ogóle nie martwię się o wydajność. Martwię się o problemy związane z utrzymaniem i projektowaniem.

skiphoppy
źródło
12
StringBuilder.append () jest lepszy niż plus przy wielokrotnym dodawaniu do ciągu, ponieważ za każdym razem string1 + string2przydzielasz nowy obiekt ciągu i kopiujesz znaki z obu ciągów wejściowych. Jeśli dodajesz n Ciągów razem, będziesz robił alokacje n-1 i około (n ^ 2) / 2 znaków kopii. Z drugiej strony StringBuilder kopiuje i realokuje rzadziej (choć nadal działa zarówno po przekroczeniu rozmiaru bufora wewnętrznego). Teoretycznie istnieją przypadki, w których kompilator może przekonwertować +, aby użyć StringBuilder, ale w praktyce kto wie.
Laurence Gonsalves
5
Za każdym razem, gdy wskakuję do debuggera, + jest konwertowane na wywołanie StringBuilder.append () w Javie 1.5. Koledzy myląco powiedzieli mi, że StringBuilder ma błąd, ponieważ debugują w kodzie, który nie wydaje się go wywoływać i tam się kończy.
skiphoppy
4
Zobacz także: stackoverflow.com/questions/782810/…
Michael Myers
61
Zauważ, że literał łańcuchowy złożony z „abc \ n” + „def \ n” itd. Nie używa StringBuilder: kompilator skleja je razem i umieszcza w pliku .class jako pojedynczy literał, tak samo jak w przypadku innych typów ciągłe składanie.
araqnid
6
Większość IDE obsługuje wprowadzanie ciągów wieloliniowych. to znaczy. po prostu wpisz lub wklej co chcesz w ciąg znaków „”, a doda \ n i „+” zgodnie z wymaganiami. np. Mogę wkleić 40 wierszy tekstu do ciągu i IDE je dla ciebie rozwiąże.
Peter Lawrey

Odpowiedzi:

118

Stephen Colebourne stworzył propozycję dodania ciągów wieloliniowych w Javie 7.

Ponadto Groovy ma już obsługę ciągów wieloliniowych .

Paul Morie
źródło
14
Proces Project Coin dla ulepszeń w Javie obejmował ciągi wieloliniowe mail.openjdk.java.net/pipermail/coin-dev/2009- February / ... Został odrzucony przez Oracle blogs.sun.com/darcy/entry/project_coin_final_five .
JodaStephen
8
jakaś zmiana w 2012 roku?
Ilia G,
13
Niestety nie wydaje się, aby znalazło się w specyfikacji.
namuol
3
Link blogs.sun.com jest zepsuty, ale myślę, że treść jest teraz na blogs.oracle.com/darcy/entry/project_coin_final_five .
Donal Fellows
8
Wydaje się, że od stycznia 2018 r. Społeczność ponownie rozważa ciągi wieloliniowe. openjdk.java.net/jeps/326
Shane Gannon
485

Wygląda na to, że chcesz zrobić wielowierszowy literał, który nie istnieje w Javie.

Najlepszą alternatywą będą ciągi, które są po prostu +razem. Niektóre inne opcje, o których wspominali ludzie (StringBuilder, String.format, String.join) byłyby preferowane tylko, jeśli zacząłeś od tablicy ciągów.

Rozważ to:

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

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

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

Jeśli chcesz nową linię dla swojego konkretnego systemu, musisz albo użyć System.lineSeparator(), albo możesz użyć %nw String.format.

Inną opcją jest umieszczenie zasobu w pliku tekstowym i po prostu odczytanie zawartości tego pliku. Byłoby to preferowane w przypadku bardzo dużych ciągów, aby uniknąć niepotrzebnego wzdęcia plików klas.

Kip
źródło
246
Co więcej, pierwsza wersja zostanie automatycznie połączona przez kompilator, ponieważ wszystkie ciągi znaków są znane w czasie kompilacji. Nawet jeśli łańcuchy nie są znane w czasie kompilacji, nie jest wolniejsze niż StringBuilder lub String.format (). Jedynym powodem, dla którego należy unikać łączenia z +, jest to, że robisz to w pętli.
Michael Myers
23
Problem z String.formatwersją polega na tym, że format musi być zsynchronizowany z liczbą wierszy.
Bart van Heukelom,
4
String.format nie jest wydajny w porównaniu do dwóch innych przykładów
cmcginty
10
Ta odpowiedź jest bardzo nieodpowiednim rozwiązaniem na dane pytanie. Mamy 2000 linii makr SAS lub paczki po 200 linii zapytań SQL, które chcemy skopiować i wkleić. Niedorzeczne jest sugerowanie, że używamy konkat + "" do przekształcenia tekstu wielowierszowego w załączniki StringBuffer.
Błogosławiony Geek
21
@BlessedGeek: pytanie dotyczyło tego, jakie opcje były dostępne w języku Java. Nie wspomniał nic o typie danych wchodzących w ciąg. Jeśli istnieje lepsze rozwiązanie, możesz opublikować je jako odpowiedź. Wygląda na to, że rozwiązanie Josha Currena byłoby lepsze w twojej sytuacji. Jeśli jesteś po prostu zdenerwowany, że język nie obsługuje literałów wieloliniowych, to jest to złe miejsce do narzekań na to.
Kip
188

W Eclipse, jeśli włączysz opcję „Escape tekstu podczas wklejania do literału ciągów” (w Preferencje> Java> Edytor> Pisanie) i wkleisz ciąg wielu wierszy z cudzysłowami, automatycznie doda "i \n" +dla wszystkich twoich wierszy.

String str = "paste your text here";
Monir
źródło
15
intelij robi to również domyślnie po wklejeniu do „” s
Bob B
Czy generalnie zostawiasz to, \rco Eclipse wprowadza w systemie Windows?
Noumenon,
99

Jest to stary wątek, ale nowym dość eleganckim rozwiązaniem (z tylko 4, może 3 małymi wadami) jest użycie niestandardowej adnotacji.

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

Projekt zainspirowany tą pracą jest hostowany na GitHub:

https://github.com/benelog/multiline

Przykład kodu 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);
  }
}

Wady są

  1. że musisz aktywować odpowiedni (dostarczony) procesor adnotacji.
  2. tej zmiennej String nie można zdefiniować jako zmiennej lokalnej. Sprawdź projekt Raw Raw Literals, w której można zdefiniować zmienne jako zmienne lokalne
  3. ten ciąg nie może zawierać innych zmiennych, jak w Visual Basic .Net z XML literal ( <%= variable %>) :-)
  4. literał łańcuchowy jest ograniczony komentarzem JavaDoc (/ **)

Prawdopodobnie musisz skonfigurować Eclipse / Intellij-Idea, aby nie formatował automatycznie swoich komentarzy Javadoc.

Może się to wydawać dziwne (komentarze Javadoc nie mają na celu osadzania niczego poza komentarzami), ale ponieważ ten brak ciągów wielowierszowych w Javie jest naprawdę denerwujący, uważam to za najgorsze rozwiązanie.

SRG
źródło
Czy to wymaga, aby klasa wykorzystująca ciąg multilinii była ostateczna? Ponadto, czy wymagana jest jakakolwiek konfiguracja podczas tworzenia i wykonywania kodu z Eclipse? Referencyjny adres URL wspomina o wymaganiach dotyczących konfiguracji Maven do przetwarzania adnotacji. Nie mogę ustalić, co może być potrzebne w Eclipse.
David
Adnotacja jest znośna - ale wydaje się, że istniała również silna zależność od maven? Ta część odbiera dużą część wartości heredoc, które mają uprościć zarządzanie małymi fragmentami tekstu.
javadba
3
Możesz to zrobić całkowicie w zaćmieniu. Link opublikowany powyżej przez @SRG prowadzi do tego linku . Jeśli używasz Eclipse, to minuta instalacji i działa.
Michael Plautz
4
To prawdopodobnie największy hack, jaki kiedykolwiek widziałem. EDYCJA: Nieważne ... patrz odpowiedź Boba Albrightsa.
Llew Vallis,
1
Zrobiłem rozszerzenie tego projektu i stworzyłem nowy, w którym obsługiwane są zmienne lokalne, spójrz na projekt
deFreitas
62

Inną opcją może być przechowywanie długich ciągów w pliku zewnętrznym i wczytywanie pliku do ciągu.

Josh Curren
źródło
13
Dokładnie. Duże ilości tekstu nie należą do źródła Java; zamiast tego użyj pliku zasobów o odpowiednim formacie, załadowanego za pomocą wywołania Class.getResource (String).
erickson
5
Dobrze! Możesz użyć Locale + ResourceBundle również do łatwego załadowania tekstu I18N, a następnie wywołanie String.format () przeanalizuje „\ n” jako nowe znaki :) Przykład: String readyStr = String.parse (resourceBundle.getString („ wprowadzenie"));
ATorras,
62
Nie powinieneś eksternalizować Ciągu tylko dlatego, że jest on wielowierszowy. Co jeśli mam wyrażenie regularne, które chcę podzielić na wiele wierszy z komentarzami? W Javie wygląda brzydko. @Składni dla C # jest znacznie czystszy.
Jeremy Stein,
8
Skiphoppy nie chce zawracać sobie głowy zajmowaniem się plikami, aby użyć stałej długości ciągu akapitu. Ciągle używam ciągów wielowierszowych w C ++, osadzonych w kodzie źródłowym, tam gdzie chcę.
Tim Cooper,
9
Łał. Nie mogę uwierzyć, że C ++ jest w tej kwestii lepszy niż Java! Uwielbiam wieloliniowe stałe ciągów, które w niektórych przypadkach należą do źródła.
Użytkownik1
59

Jest to coś, czego nigdy nie powinieneś używać bez myślenia o tym, co robi. Ale w przypadku skryptów jednorazowych wykorzystałem to z wielkim sukcesem:

Przykład:

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

Kod:

// 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
źródło
15
Wymaga wysyłki kodu Java dla klasy z ostatecznym plikiem binarnym. Hmm
Thorbjørn Ravn Andersen
23
mogę sobie wyobrazić reakcję współpracowników, gdy próbuję sprawdzić coś takiego w ...
Landon Kuhn
15
+1. Trochę wyobraźni głosujących. Jest to przydatna konstrukcja do pisania małych narzędzi, przypadków testowych, a nawet w kontrolowanych środowiskach prod. Jest to różnica między porzuceniem java do Ruby / Python / etc a pozostaniem tutaj.
javadba
1
Świetne rozwiązanie, ale niestety nie będzie działać na Androida, ponieważ zostanie wykonane na emulatorze lub na prawdziwym urządzeniu i nie ma tam kodu źródłowego.
evgeny.myasishchev
Być może jest to Java 8 prob lub coś innego, ale classLoader w tym przykładzie nie istnieje. Próbowałem użyć MyClass.class.getResourceAsStream (...), ale zawsze zwraca wartość null. Byłoby to świetne szybkie rozwiązanie do testowania!
Nick
54

String.join

Java 8 dodała nową metodę statyczną, java.lang.Stringktóra oferuje nieco lepszą alternatywę:

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

Użyj tego:

String s = String.join(
    System.getProperty("line.separator"),
    "First line.",
    "Second line.",
    "The rest.",
    "And the last!"
);
icza
źródło
5
Czyste i czyste rozwiązanie! Brak zależności od IDE i preprocesora! Nie "\n"jest wymagana żadna instrukcja i jest świadomy przenośności!
Siu Ching Pong -Asuka Kenji-
1
Rozumiem, że mój komentarz jest bezużyteczny, ale jest tak przemyślany, że szuka hacków dla tak podstawowej rzeczy, jak literał wieloliniowy. Dlaczego, do cholery, java nadal nie może dodać tego do specyfikacji?
dmitry
22

JEP 355: Bloki tekstowe ( wersja zapoznawcza) ma na celu pokrycie tej funkcjonalności, obecnie jest ukierunkowane na JDK 13 jako funkcję podglądu. Pozwalając napisać coś takiego:

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

Poprzednio do tego JEP, w JDK12, JEP 326: Raw String Literals miał na celu implementację podobnej funkcji, ale ostatecznie został wycofany.

alostale
źródło
2
Porzucili
2
Bloki tekstowe są teraz częścią Java 13.
ZhekaKozlov
19

Jeśli zdefiniujesz swoje ciągi w pliku właściwości, będzie wyglądać znacznie gorzej. IIRC będzie wyglądać następująco:

string:text\u000atext\u000atext\u000a

Generalnie rozsądnym pomysłem jest nie osadzanie dużych łańcuchów w źródle. Możesz je załadować jako zasoby, być może w formacie XML lub czytelnym formacie tekstowym. Pliki tekstowe można odczytać w czasie wykonywania lub skompilować do źródła Java. Jeśli w końcu umieścisz je w źródle, sugeruję umieszczenie go +z przodu i pominięcie niepotrzebnych nowych wierszy:

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

Jeśli masz nowe linie, możesz chcieć zastosować metodę łączenia lub formatowania:

final String text = join("\r\n"
    ,"text"
    ,"text"
    ,"text"
);
Tom Hawtin - tackline
źródło
17

Plusy są konwertowane na StringBuilder.append, z wyjątkiem sytuacji, gdy oba ciągi są stałymi, dzięki czemu kompilator może je łączyć w czasie kompilacji. Tak przynajmniej jest w kompilatorze Sun i podejrzewam, że większość, jeśli nie wszystkie inne kompilatory, zrobiłyby to samo.

Więc:

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

zwykle generuje dokładnie ten sam kod, co:

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

Z drugiej strony:

String c="Hello"+"Goodbye";

jest taki sam jak:

String c="HelloGoodbye";

Oznacza to, że nie ma kary za łamanie literałów łańcuchowych w wielu wierszach ze znakami plus dla czytelności.

Sójka
źródło
4
technicznie, w pierwszym przykładzie generuje coś więcej: String c = new StringBuilder (). append (a) .append (b) .toString (); Różnica polega na tym, że tymczasowy konstruktor ciągów jest poza zakresem i kwalifikuje się do wyrzucania elementów bezużytecznych natychmiast po wierszu Łańcuch c = ..., podczas gdy konstruktor ciągów "temp" pozostałby trochę dłużej.
Kip
Prawdziwe. Chodzi mi oczywiście o rozróżnienie, kiedy funkcja jest wywoływana w czasie wykonywania, a kiedy praca może być wykonana w czasie kompilacji. Ale masz rację.
Jay
15

W IntelliJ IDE wystarczy wpisać:

""

Następnie umieść kursor w cudzysłowie i wklej ciąg. IDE rozwinie go do wielu połączonych linii.

nurettin
źródło
11

Niestety Java nie ma wieloliniowych literałów łańcuchowych. Musisz albo połączyć literały łańcuchowe (używając + lub StringBuilder to dwa najczęstsze podejścia do tego), albo wczytać łańcuch z osobnego pliku.

W przypadku dużych wieloliniowych literałów łańcuchowych byłbym skłonny użyć osobnego pliku i odczytać go za pomocą getResourceAsStream()(metoda Classklasy). Ułatwia to znalezienie pliku, ponieważ nie musisz martwić się o bieżący katalog w porównaniu z miejscem, w którym został zainstalowany kod. Ułatwia także pakowanie, ponieważ plik można zapisać w pliku jar.

Załóżmy, że jesteś w klasie o nazwie Foo. Po prostu zrób coś takiego:

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

Inną irytacją jest to, że Java nie ma standardowej metody „odczytywania całego tekstu z tego czytnika do ciągu”. Jednak dość łatwo jest napisać:

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
źródło
Robię to samo. Możesz użyć commons-io, aby łatwiej odczytać zawartość pliku (za pomocą „FileUtils.readFileToString (plik pliku)”).
SRG,
To nieprawda, że java nie ma standardowej metody odczytu wszystkich tekstów ... - od Java 7 możesz używać Files.readAllLines (Path)
ccpizza
10
String newline = System.getProperty ("line.separator");
string1 + newline + string2 + newline + string3

Ale najlepszą alternatywą jest użycie String.format

String multilineString = String.format("%s\n%s\n%s\n",line1,line2,line3);
Tomek
źródło
Uważam, że usuwa znaki plus i cudzysłowy, dzięki czemu jest bardziej czytelny, szczególnie gdy są więcej niż 3 linie. Jednak nie tak dobry jak String.format.
Tom
2
Przykład programu Stringbuilder jest co najmniej tak samo nieczytelny. Nie zapominaj również, że „\ n” nie zawsze jest nowym wierszem, ale jest odpowiedni dla maszyn z systemem Linux i Unix.
Stefan Thyberg
Dodatkowo chciałem tylko wspomnieć o istnieniu StringBuilder.
Tom
4
Zamiana jednego znaku plus na sześcioznakową nazwę metody i nawiasy nie wydaje mi się bardziej czytelna, chociaż najwyraźniej nie tylko ty tak myślisz. Nie usuwa jednak cytatów. Nadal tam są.
skiphoppy
9

Ponieważ Java nie obsługuje (jeszcze) natywnych ciągów wieloliniowych, jedynym sposobem na razie jest włamanie się do niego przy użyciu jednej z wyżej wymienionych technik. Zbudowałem następujący skrypt Pythona, korzystając z niektórych sztuczek wymienionych powyżej:

import sys
import string
import os

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

Umieść to w pliku o nazwie javastringify.py, a swój ciąg w pliku mystring.txt i uruchom w następujący sposób:

cat mystring.txt | python javastringify.py

Następnie możesz skopiować wynik i wkleić go do edytora.

Zmodyfikuj to w razie potrzeby, aby obsłużyć specjalne przypadki, ale działa to na moje potrzeby. Mam nadzieję że to pomoże!

scorpiodawg
źródło
9

Możesz użyć kodu scala, który jest zgodny z Javą i pozwala na stosowanie ciągów wielowierszowych zawartych w „” „:

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

(zwróć uwagę na cytaty w ciągu) i z Java:

String s2 = foobar.SWrap.bar ();

Czy to jest wygodniejsze ...?

Innym podejściem, jeśli często posługujesz się długim tekstem, który powinien być umieszczony w kodzie źródłowym, może być skrypt, który pobiera tekst z zewnętrznego pliku i otacza go jako ciąg multiline-java:

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

dzięki czemu możesz łatwo wyciąć i wkleić go do źródła.

użytkownik nieznany
źródło
8

Możesz połączyć swoje załączniki za pomocą osobnej metody, takiej jak:

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

Tak czy inaczej, wolisz StringBuildernotację plus.

54579
źródło
5
Dlaczego wolę StringBuilder od notacji plus?
skiphoppy
10
Wydajność, a raczej często mylona próba.
Michael Myers
2
Wydaje mi się, że próba wydajności opiera się na fakcie, że kompilator Java implementuje operator konkatenacji łańcuchów za pomocą StringBuilder (StringBuffer w kompilatorach wcześniejszych niż 1.5). Istnieje stary, ale dobrze znany artykuł, w którym stwierdza się, że korzystanie z StringBuffer (lub StringBuilder, w niektórych sytuacjach przynosi korzyści w zakresie wydajności). Oto link: java.sun.com/developer/JDCTechTips/2002/tt0305.html
Paul Morie
4
Tylko wtedy, gdy kompilator nie może tego zrobić. W przypadku literałów i stałych, jeśli użyjesz znaku plus, konkatenacja zostanie wykonana w czasie kompilacji. Użycie StringBuilder zmusza go do uruchomienia w czasie wykonywania, więc jest to nie tylko więcej pracy, jest wolniejsze.
stycznia
7

Tak naprawdę, to najczystsza implementacja, jaką do tej pory widziałem. Wykorzystuje adnotację do konwersji komentarza na zmienną łańcuchową ...

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

Zatem końcowy wynik jest taki, że zmienna html zawiera łańcuch wielowierszowy. Bez cudzysłowów, plusów, przecinków, po prostu czysty ciąg.

To rozwiązanie jest dostępne pod następującym adresem URL ... http://www.adrianwalker.org/2011/12/java-multiline-string.html

Mam nadzieję, że to pomaga!

Rodney P. Barbati
źródło
Ten procesor adnotacji wymaga dokładniejszego sprawdzania,
Tripp Kinetics
lubię to. Próbuje ją
javadba
7

Zobacz Java Stringfier . W razie potrzeby zamienia tekst w blok java StringBuilder.

Leo
źródło
1
Tak, ponieważ mogę spędzić całe życie na kopiowaniu i wklejaniu na tej stronie. Mógłbym też po prostu zapisać je w pliku i załadować, ale to też nie jest idealne rozwiązanie.
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");
rev Mychajło Adamowicz
źródło
6

Alternatywą, której nie widziałem jeszcze jako odpowiedzi, jest 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();

Również fakt, że java.io.BufferedWriteristnieje newLine()metoda, nie został wspomniany.

BalusC
źródło
5

Jeśli podoba Ci się guawa google tak samo jak ja, może dać dość czystą reprezentację i miły, łatwy sposób, aby nie kodować również znaków nowej linii:

String out = Joiner.on(newline).join(ImmutableList.of(
    "line1",
    "line2",
    "line3"));
Dan Lo Bianco
źródło
5

Zastosowanie Properties.loadFromXML(InputStream). Nie ma potrzeby korzystania z zewnętrznych bibliotek.

Lepsze niż nieporządny kod (ponieważ zależy Ci na łatwości konserwacji i projektowaniu), lepiej nie używać długich ciągów.

Zacznij od przeczytania właściwości xml:

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


możesz użyć swojego ciągu wielowierszowego w bardziej konserwowalny sposób ...

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


MultiLine.xml` zostanie umieszczony w tym samym folderze 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 .: Możesz użyć <![CDATA["... "]]>dla łańcucha podobnego do xml.

jpfreire
źródło
Tak, tego też używam, świetne rozwiązanie! Przenieś SQL lub XML do zewnętrznego pliku właściwości XML. Nie psuje kodu. :)
Laszlo Lugosi
To nie odpowiada na pytanie. heredoc są z definicji w pliku . Chodzi o to, aby trzymać go w jednym miejscu.
javadba
5

W kompilacji # 12 wczesnego dostępu JDK / 12 można teraz używać ciągów wielowierszowych w Javie w następujący sposób:

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

i to powoduje następujące wyniki:

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

Edycja: Przełożono na java 13

Naman
źródło
1
Oto sposób wypróbowania go za pomocą maven
Naman
2
Jak cybersoft mówi w innym komentarzu, dosłowne ciągi literałów (JEP326) zostały usunięte z ostatecznego JDK12, ale utworzono kolejny JEP w celu dodania „bloków tekstowych”, które można wykonać jako podgląd w JDK 13
Manuel Romeiro
4

Dość wydajnym i niezależnym od platformy rozwiązaniem byłoby użycie właściwości systemowej dla separatorów linii i klasy StringBuilder do budowy ciągów:

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
źródło
4

Jedna dobra opcja.

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
źródło
4

To bardzo częste pytanie, dlatego postanowiłem zamienić tę odpowiedź również w artykuł .

Java 13 i nowsze wersje

Ciągi wielowierszowe są teraz obsługiwane w Javie przez bloki tekstowe . W Javie 13 i 14 ta funkcja wymaga ustawienia tej ––enable–previewopcji podczas budowania i uruchamiania projektu. Sprawdź tę dokumentację Java więcej szczegółów.

Przed wersją Java 13 możesz napisać zapytanie:

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

Dzięki blokom tekstowym Java 13 możesz przepisać to zapytanie w następujący sposób:

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

O wiele bardziej czytelny, prawda?

Obsługa IDE

IntelliJ IDEA zapewnia obsługę transformacji starszych Stringbloków konkatenacji do nowego Stringformatu wielowierszowego :

Obsługa bloków tekstowych IntelliJ IDEA

JSON, HTML, XML

Multilinia Stringjest szczególnie przydatna podczas pisania JSON, HTML lub XML.

Rozważ ten przykład, używając Stringkonkatenacji do zbudowania literału łańcucha 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" +
        "       }" +
        "   ]" +
        "}"
    )
);

Ledwo możesz odczytać JSON z powodu uciekających znaków i mnóstwa podwójnych cudzysłowów i znaków plus.

W przypadku bloków tekstowych Java obiekt JSON można zapisać w następujący sposób:

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

Odkąd użyłem C # w 2004 roku, chciałem mieć tę funkcję w Javie, a teraz w końcu ją mamy.

Vlad Mihalcea
źródło
3

Zdefiniować mój ciąg w pliku właściwości?

Ciągi wielowierszowe nie są dozwolone w plikach właściwości. Możesz użyć \ n w plikach właściwości, ale nie wydaje mi się, żeby to było dobre rozwiązanie w twoim przypadku.

Wyrko
źródło
Wartość w pliku właściwości może obejmować wiele wierszy: wystarczy zakończyć wszystkie wiersze, ale ostatni odwrotnym ukośnikiem. Pozostawia to problem z tym, czego używasz jako separatora linii, ponieważ jest to specyficzne dla platformy. Przypuszczam, że możesz użyć prostego \ n, a następnie w kodzie, po odczytaniu właściwości, wykonaj wyszukiwanie i zamianę \ n do line.separator. To wydaje się trochę kludgey, ale myślę, że możesz napisać funkcję, która pobiera właściwość i wykonuje tę manipulację w tym samym czasie. Cóż, wszystko to zakłada, że ​​zapisujesz te ciągi do pliku, co jest dużym założeniem.
Jay
3

Sugeruję użycie narzędzia sugerowanego przez ThomasP, a następnie połączenie go z procesem kompilacji. Plik zewnętrzny jest nadal obecny, aby zawierać tekst, ale plik nie jest odczytywany w czasie wykonywania. Przepływ pracy jest wtedy:

  1. Zbuduj narzędzie „plik tekstowy do kodu Java” i sprawdź kontrolę wersji
  2. Na każdej kompilacji uruchom narzędzie dla pliku zasobów, aby utworzyć poprawione źródło Java
  3. Źródło Java zawiera nagłówek podobny class TextBlock {...do statycznego ciągu, który jest automatycznie generowany z pliku zasobów
  4. Zbuduj wygenerowany plik Java z resztą kodu
horace
źródło
2

Gdy używana jest długa seria +, tworzony jest tylko jeden StringBuilder, chyba że w czasie kompilacji zostanie określony String, w którym to przypadku nie zostanie użyty StringBuilder!

StringBuilder jest bardziej efektywny tylko wtedy, gdy do zbudowania ciągu użyto wielu instrukcji.

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

Uwaga: Tworzony jest tylko jeden 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

Aby wyjaśnić moje pytanie, nie martwię się o wydajność. Martwię się o problemy związane z utrzymaniem i projektowaniem.

Spraw, aby było to tak jasne i proste, jak to tylko możliwe.

Peter Lawrey
źródło