Багаторядковий рядок Java


516

Виходячи з Perl, я впевнений, що мені не вистачає засобів "тут-документ" для створення багаторядкової рядки у вихідному коді:

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

У Java я повинен мати громіздкі лапки та знаки плюс у кожному рядку, коли я поєдную свій багаторядковий рядок з нуля.

Які є кращі альтернативи? Визначити мій рядок у файлі властивостей?

Редагувати : Два відповіді говорять, що StringBuilder.append () є кращим порівняно з позначенням плюс. Чи може хтось детальніше пояснити, чому вони так думають? Мені це зовсім не подобається. Я шукаю шлях до того, що багаторядкові рядки не є першокласною мовною конструкцією, а це означає, що я точно не хочу замінювати мовну конструкцію першого класу (об'єднання рядків на плюс) на виклики методів.

Редагувати : Щоб уточнити моє питання далі, я взагалі не переймаюся ефективністю. Мене хвилюють проблеми з технічним обслуговуванням та дизайном.


12
StringBuilder.append () є кращим плюсом при багаторазовому додаванні до рядка, тому що кожного разу ви string1 + string2виділяєте новий об'єкт рядка та копіюєте символи з обох вхідних рядків. Якщо ви додаєте n рядків разом, ви б робили виділення n-1 і приблизно (n ^ 2) / 2 копії символів. StringBuilder, з іншого боку, копіює та перерозподіляє рідше (хоча це все одно робить обидва, коли ви перевищуєте розміри внутрішнього буфера). Теоретично існують випадки, коли компілятор міг перетворити + для використання StringBuilder, але на практиці хто це знає.
Лоранс Гонсалвс

5
Кожен раз, коли я стрибаю у відладчик, + перетворюється на виклик StringBuilder.append () на Java 1.5. У мене колеги зніяковіло говорять мені, що у StringBuilder є помилка, оскільки вони налагоджують код, який, схоже, не викликає його і закінчується.
skiphoppy


61
Зауважте, що рядковий літерал, що складається з "abc \ n" + "def \ n" тощо, не використовує StringBuilder: компілятор склеює їх і вкладає їх у файл .class як єдиний літерал, як і для інших типів постійне складання.
аракнід

6
Більшість IDE підтримують введення рядкових рядків. тобто. ви просто введіть або вставте те, що ви хочете в рядок "", і це додасть \ n і "+", як потрібно. наприклад, я можу вставити 40 рядків тексту до рядка, а IDE розсортує його для вас.
Пітер Лоурі

Відповіді:


118

Стівен Колбурн створив пропозицію щодо додавання багаторядкових рядків у Java 7.

Також у Groovy вже є підтримка багаторядкових рядків .


14
Процес проектної монети для вдосконалення Java включав багаторядкові рядки mail.openjdk.java.net/pipermail/coin-dev/2009-February/… . Її відхилили Oracle blogs.sun.com/darcy/entry/project_coin_final_five .
JodaStephen

8
будь-які зміни в 2012 році?
Ілля Г

13
На жаль, схоже, це не ввійшло в специфікацію.
namuol

3
Посилання blogs.sun.com розірвано, але я думаю, що вміст зараз знаходиться на blogs.oracle.com/darcy/entry/project_coin_final_five .
Стипендіати з

8
Здається, станом на січень 2018 року громада переглядає багаторядкові рядки. openjdk.java.net/jeps/326
Шейн Ганнон

485

Це здається, що ви хочете зробити багаторядковий літерал, якого немає на Java.

Вашою найкращою альтернативою будуть просто струнні струни + разом. Деякі інші параметри, про які люди згадували (StringBuilder, String.format, String.join), були б кращі, лише якщо ви почали з масиву рядків.

Врахуйте це:

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

Якщо ви хочете новий рядок для вашої конкретної системи, вам або потрібно скористатися System.lineSeparator(), або ви можете використовувати %nв String.format.

Інший варіант - помістити ресурс у текстовий файл і просто прочитати вміст цього файлу. Це бажано для дуже великих рядків, щоб уникнути зайвого здуття файлів класу.


246
Крім того, перша версія буде автоматично об'єднана компілятором, оскільки всі рядки відомі під час компіляції. Навіть якщо рядки не відомі під час компіляції, це не повільніше, ніж StringBuilder або String.format (). Єдина причина уникати конкатенації з + - це, якщо ви робите це в циклі.
Майкл Майерс

23
Проблема з String.formatверсією полягає в тому, що ви повинні тримати формат синхронізовано з кількістю рядків.
Барт ван Хекелом

4
String.format не є ефективним порівняно з іншими двома прикладами
cmcginty

10
Ця відповідь є дуже невідповідним вирішенням питання. У нас є 2000 ліній SAS-макросів або пучок із 200 рядкових запитів SQL, які ми хочемо скопіювати та вставити. Думати, що ми використовуємо + "" concat для перетворення цих багаторядкових текстів у додатки StringBuffer, смішно.
Благословенний гек

21
@BlessedGeek: питання, яке було про те, які варіанти доступні на мові Java. У ньому нічого не згадувалося про тип даних, що надходять у рядок. Якщо краще рішення, то ви можете опублікувати це як відповідь. Здається , рішення Джоша Керрена було б кращим для вашої ситуації. Якщо ви просто засмучені тим, що мова не підтримує багаторядкові літерали, то це неправильне місце для скарги на це.
Кіп

188

Якщо у програмі Eclipse увімкнути опцію "Escape text під час вставки в літеральний рядок" (у розділі Налаштування> Java> Редактор> Введення тексту) і вставити багаторядкові рядки з цитатами, він автоматично додаватиме "і \n" +для всіх ваших рядків.

String str = "paste your text here";

15
intelij також робить це за замовчуванням, коли ви вставляєте "" s
Bob B

Як правило, ви залишаєте програму \rEclipse в Windows?
Номенон

99

Це стара тема, але новим досить елегантним рішенням (лише 4, можливо, 3 маленькі недоліки) є використання спеціальної анотації.

Перевірка: http://www.adrianwalker.org/2011/12/java-multiline-string.html

Проект, натхненний цією роботою, розміщується на GitHub:

https://github.com/benelog/multiline

Приклад коду 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);
  }
}

Недоліки є

  1. що вам потрібно активувати відповідний (наданий) процесор анотацій.
  2. що змінна String не може бути визначена як локальна змінна Project Check Raw String Literals де ви можете визначити змінні як локальні змінні
  3. що String не може містити інші змінні, як у Visual Basic .Net з літералом XML (<%= variable %> ) :-)
  4. що Stral Literal розмежовано коментарем JavaDoc (/ **)

І, ймовірно, вам доведеться налаштувати Eclipse / Intellij-Idea, щоб не переформатувати автоматично ваші коментарі Javadoc.

Це може здатися дивним (коментарі Javadoc не призначені для вбудовування нічого іншого, крім коментарів), але оскільки ця відсутність багаторядкових рядків у Java справді дратує, зрештою, я вважаю це найменш гіршим рішенням.


Це вимагає, щоб клас, використовуючи рядок багаторядкових, був остаточним? Крім того, чи потрібна будь-яка настройка при розробці та виконанні коду з Eclipse? Довідкова URL-адреса згадує вимоги до налаштування Maven для обробки анотацій. Я не можу зрозуміти, що може знадобитися, якщо таке є у Eclipse.
Девід

Анотація є придатною для життя - але, здається, також існувала жорстка залежність від Maven? Ця частина забирає значну частину цінності гередока, яка полягає у спрощенні управління невеликими фрагментами тексту.
javadba

3
Це можна зробити цілком у затемненні. Посилання, яке розмістив @SRG вище, вказує на це посилання . Якщо ви використовуєте затемнення, то хвилина налаштування і воно працює.
Майкл Плаут

4
Це, мабуть, найбільший злом, який я коли-небудь бачив. EDIT: Nevermind ... див. Відповідь Боб Альбрійтс.
Llew Vallis

1
Я зробив розширення цього проекту, створив нову, яку підтримують локальні змінні, подивіться на проект
deFreitas

62

Іншим варіантом може бути збереження довгих рядків у зовнішньому файлі та зчитування файлу в рядку.


13
Саме так. Велика кількість тексту не належить до джерела Java; використовувати замість цього файлу ресурсів відповідного формату, завантаженого за допомогою виклику Class.getResource (String).
еріксон

5
Правильно! Ви також можете використовувати Locale + ResourceBundle's, щоб легко завантажити текст I18N, і тоді виклик String.format () проаналізує "\ n" 'нові рядки :) Приклад: String readyStr = String.parse (resourceBundle.getString (" вступ »));
Аторрас

62
Вам не потрібно було б екстерналізувати String лише тому, що він багаторядковий. Що робити, якщо у мене є регулярний вираз, який я хочу розбити на кілька рядків з коментарями? На Яві це виглядає некрасиво. @Синтаксис C # набагато чистіше.
Джеремі Штейн

8
Skiphoppy не хоче займатись накладними обробками файлів, просто використовувати постійну строку довжини абзацу. Я постійно використовую багаторядкові рядки в C ++, вбудованому у свій вихідний код, куди я їх хочу.
Тім Купер

9
Ого. Я не можу повірити, що C ++ насправді кращий за Java у цьому питанні! Я люблю багаторядкові струнні константи, і вони дійсно належать до джерела в деяких випадках.
Користувач1

59

Це те, чого ти ніколи не повинен використовувати, не замислюючись про те, що це робить. Але для разових сценаріїв я користувався цим з великим успіхом:

Приклад:

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

Код:

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

15
Потрібна доставка коду Java для класу з остаточним двійковим кодом. Хм.
Thorbjørn Ravn Andersen

23
я можу уявити собі реакцію своїх колег, коли я намагаюся перевірити щось подібне у ...
Ландон Кун

15
+1. Деякі брак уяви у людей, які голосують проти. Це корисна конструкція для написання невеликих утиліт, тестових кейсів і навіть у контрольованих умовах продукту. Це різниця між тим, що випав з Java в рубін / пітон / тощо або залишився тут.
javadba

1
Відмінне рішення, але, на жаль, не працюватиме для android, оскільки він буде виконуватися на емуляторі чи реальному пристрої, а вихідного коду там немає.
evgeny.myasishchev

Можливо, це завдання Java 8 або щось інше, але classLoader у прикладі не існує. Я спробував використовувати MyClass.class.getResourceAsStream (...), але він завжди повертається до нуля. Було б чудовим швидким рішенням для тестування!
Нік

54

String.join

Java 8 додала новий статичний метод, java.lang.Stringякий пропонує трохи кращу альтернативу:

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

Використовуючи його:

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

5
Охайний і чистий розчин! Ніякої залежності від IDE та препроцесора! Посібник "\n"не потрібен, і він знає портативність!
Сіу Чін Понг -Asuka Kenji-

1
Я розумію, що мій коментар марний, але він настільки спокійний, щоб шукати хакі для такої основної речі, як багаторядковий буквений рядок. Чому чорт не може все-таки Ява додати це до специфікації?
Дмитрій

22

JEP 355: Текстові блоки (попередній перегляд) мають на меті охопити цю функціональність, вона в даний час націлена на JDK 13 як функцію попереднього перегляду. Дозволяє написати щось на кшталт:

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

До цього JEP, в JDK12, JEP 326: Raw String Literals мали на меті реалізувати подібну функцію, але її остаточно було вилучено.


2
Вони відмовилися від цієї підтримки
deFreitas

2
Текстові блоки тепер є частиною Java 13.
ЖекаКозлов

19

Якщо ви визначите свої рядки у файлі властивостей, це буде виглядати набагато гірше. IIRC, це буде виглядати так:

string:text\u000atext\u000atext\u000a

Як правило, розумна ідея не вставляти великі рядки в джерело. Можливо, ви хочете завантажити їх як ресурси, можливо, у XML чи читаному текстовому форматі. Текстові файли можна читати під час виконання або компілювати у джерело Java. Якщо ви в кінцевому підсумку розміщуєте їх у джерелі, я пропоную розмістити +внизу та пропустити непотрібні нові рядки:

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

Якщо у вас є нові рядки, вам може знадобитися якийсь метод приєднання або форматування:

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

17

Плюси перетворюються в StringBuilder.append, за винятком випадків, коли обидва рядки є константами, і компілятор може їх комбінувати під час компіляції. Принаймні, саме так у компілятора Sun, і я б підозрював більшість, якби не всі інші компілятори зробили те саме.

Тому:

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

зазвичай генерує абсолютно той самий код, що і:

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

З іншої сторони:

String c="Hello"+"Goodbye";

те саме, що:

String c="HelloGoodbye";

Тобто, за переривання ваших рядкових літералів по декількох рядках плюс знаки для читабельності немає жодного штрафу.


4
щоб бути технічним, у першому прикладі він створює щось на кшталт: String c = new StringBuilder (). append (a) .append (b) .toString (); Різниця полягає в тому, що тимчасовий конструктор рядків виходить за межі і придатний для збору сміття одразу після рядка String c = ..., тоді як "temp" Builder рядків залишиться дещо довше.
Кіп

Правда. Моя думка, звичайно, полягає в тому, щоб розрізнити, коли функція викликається під час виконання, а коли робота може бути виконана під час компіляції. Але ти прав.
Джей

15

У IntelliJ IDE вам просто потрібно набрати:

""

Потім розташуйте курсор всередині лапок і вставте рядок. IDE розширить його на кілька з’єднаних ліній.


11

На жаль, у Java немає багаторядкових літеральних рядків. Вам або потрібно об'єднати літеральні рядки (використовуючи + або StringBuilder як два найпоширеніших підходи до цього) або прочитати рядок з окремого файлу.

Для великих багаторядкових літеральних рядків я схильний використовувати окремий файл і читати його за допомогою getResourceAsStream()(методClass класу). Це полегшує пошук файлу, оскільки вам не доведеться турбуватися про поточний каталог, порівняно з тим, де був встановлений ваш код. Це також полегшує упаковку, оскільки ви можете фактично зберігати файл у файлі вашої банку.

Припустимо, ви в класі під назвою Foo. Просто зробіть щось подібне:

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

Ще одне роздратування полягає в тому, що у Java немає стандартного методу "прочитати весь текст з цього" Читателя в рядок ". Це досить просто написати:

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

Я роблю те саме. Ви можете використовувати commons-io для легшого читання вмісту файлу (з "FileUtils.readFileToString (Файл файлу)").
СРГ

Неправда більше про Java не має стандартного методу читання всіх текстів ... - оскільки Java 7 ви можете використовувати Files.readAllLines (Path)
ccpizza

10
String newline = System.getProperty ("line.separator");
string1 + newline + string2 + newline + string3

Але найкраща альтернатива - використовувати String.format

String multilineString = String.format("%s\n%s\n%s\n",line1,line2,line3);

На мою думку, він видаляє знаки плюс і лапки, роблячи його легше для читання, особливо коли є лише 3 рядки. Не так добре, як String.format, хоча.
Том

2
Приклад Stringbuilder - принаймні не начитаний. Крім того, не забувайте, що "\ n" не завжди є новим рядком, але це нормально для машин Linux та Unix.
Стефан Тіберг

Плюс, просто хотілося згадати про існування StringBuilder.
Том

4
Заміна знака плюс на ім'я методу шести символів та круглих дужок не виглядає для мене більш читабельним, хоча, мабуть, ви не єдиний, хто так думає. Однак це не видаляє цитати. Вони все ще є.
skiphoppy

9

Оскільки Java не (поки) не підтримує багаторядкові рядки, єдиний спосіб наразі - зламати її за допомогою однієї з вищезгаданих методик. Я створив наступний сценарій Python, використовуючи деякі згадані вище трюки:

import sys
import string
import os

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

Помістіть це у файл з назвою javastringify.py та рядок у файл mystring.txt та запустіть його так:

cat mystring.txt | python javastringify.py

Потім можна скопіювати вихід і вставити його у свій редактор.

Змініть це за необхідності для вирішення будь-яких спеціальних випадків, але це працює для моїх потреб. Сподіваюся, це допомагає!


9

Ви можете використовувати шкала-код, сумісний з java, і дозволяє багаторядкові рядки, додані до "" ":

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

(зверніть увагу на лапки всередині рядка) та з Java:

String s2 = foobar.SWrap.bar ();

Це зручніше ...?

Інший підхід, якщо ви часто обробляєте довгий текст, який повинен бути розміщений у вихідному коді, може бути скриптом, який бере текст із зовнішнього файлу та обертає його як рядок, що містить шматок ліній Java:

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

щоб ви могли легко вирізати та вставити його у свій джерело.


8

Ви можете об'єднати свої додатки окремим методом, наприклад:

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

У будь-якому випадку, віддайте перевагу StringBuilderпозначенням плюс.


5
Чому я віддаю перевагу StringBuilder перед позначенням плюс?
skiphoppy

10
Ефективність, а точніше - часто неправильна спроба на це.
Майкл Майерс

2
Спроба ефективності базується на тому, що компілятор Java реалізує оператор конкатенації рядків за допомогою StringBuilder (StringBuffer у компіляторах до 1.5). Існує стара, але добре відома стаття, в якій говориться про те, що в певних ситуаціях використання StringBuffer (або StringBuilder, зараз) є переваги продуктивності. Ось посилання: java.sun.com/developer/JDCTechTips/2002/tt0305.html
Пол Морі

4
Тільки тоді, коли компілятор не може цього зробити. Для літералів і констант, якщо ви використовуєте знак плюс, конкатенація проводиться під час компіляції. Використання StringBuilder змушує це статися під час виконання, тому це не тільки більше роботи, але і повільніше.
johncip

7

Насправді, наступне - це найчистіша реалізація, яку я бачив досі. Він використовує примітку для перетворення коментаря в рядкову змінну ...

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

Отже, кінцевим результатом є те, що змінна html містить рядок багаторядкових. Ніяких лапок, ніяких плюсів, ніяких коми, просто чистий рядок.

Це рішення доступне за наступною URL-адресою ... http://www.adrianwalker.org/2011/12/java-multiline-string.html

Сподіваюся, що це допомагає!


Цей процесор анотацій потребує більш надійної перевірки,
Tripp Kinetics

мені це подобається. Спробуй це
javadba

7

Див. Розділ Java Stringfier . Перетворює текст у Java-блок StringBuilder, уникаючи необхідності.


1
Так, тому що я можу витратити своє життя копію та вставлення на цей сайт. Я також міг би просто зберегти їх у файл і завантажити це, але це теж не є ідеальним рішенням.
ммм

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

6

Альтернатива, яку я ще не бачив як відповідь, - це 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();

Також не згадується той факт, що java.io.BufferedWriterмає newLine()метод.


5

Якщо вам подобається гуава google настільки ж, як і мені, вона може дати досить чисте уявлення та приємний простий спосіб також не жорстко кодувати ваші символи нового рядка:

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

5

Використовуйте Properties.loadFromXML(InputStream). Зовнішні губки не потрібні.

Краще, ніж брудний код (оскільки ретельність та дизайн - це ваша проблема), бажано не використовувати довгі рядки.

Почніть з читання xml-властивостей:

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


тоді ви можете використовувати свою багаторядкову рядок більш доцільним способом ...

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


MultiLine.xml` розміщується в одній папці 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 .: Ви можете використовувати <![CDATA["... "]]>для xml-рядка.


Так, це і я використовую, чудове рішення! Перемістіть SQL або XML у зовнішній файл властивостей XML. Це не псує код. :)
Ласло Лугосі

Це не дає відповіді на запитання. heredoc є за визначенням у файлі . Сенс у тому, щоб тримати його в одному місці.
javadba

5

З JDK / 12 збірки раннього доступу # 12 тепер можна використовувати багаторядкові рядки на Java таким чином:

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

і це призводить до наступного результату:

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

Редагувати: Перенесено на java 13



2
Як говорить кіберсофт в іншому коментарі, з остаточного JDK12 було видалено необроблені рядкові літерали (JEP326), але створено ще один JEP, щоб додати "Текстові блоки", які можуть бути виконані в якості попереднього перегляду в JDK 13
Мануель Ромейро

4

Досить ефективним та незалежним від платформи рішенням буде використання властивості системи для розділювачів рядків та класу StringBuilder для створення рядків:

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

4

Один хороший варіант.

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

    }

4

Це дуже поширене питання, тому я вирішив перетворити цю відповідь також у статтю .

Java 13 і пізніше

Багаторядкові рядки тепер підтримуються на Java через текстові блоки . У Java 13 та 14 ця функція вимагає встановити ––enable–previewпараметр під час створення та запуску проекту. Перегляньте цю документацію Java для отримання більш детальної інформації.

Тепер, перш ніж Java 13, ви можете написати запит:

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

Завдяки текстовим блокам Java 13 ви можете переписати цей запит наступним чином:

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

Набагато легше читати, правда?

Підтримка IDE

IntelliJ IDEA забезпечує підтримку перетворення застарілих Stringблоків конкатенації у новий багаторядковий Stringформат:

Підтримка текстових блоків IntelliJ IDEA

JSON, HTML, XML

Багаторядкова Stringособливо корисна при написанні JSON, HTML або XML.

Розглянемо цей приклад, використовуючи 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" +
        "       }" +
        "   ]" +
        "}"
    )
);

Ви ледве можете прочитати JSON через втікаючі символи та велику кількість подвійних лапок та знаків плюс.

З текстовими блоками Java об’єкт 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
               }
           ]
        }
        """
    )
);

З тих пір, як я використовував C # у 2004 році, я хотів мати цю функцію на Java, і тепер ми її нарешті маємо.


3

Визначити мій рядок у файлі властивостей?

У файлах властивостей рядки рядків не дозволяються Ви можете використовувати \ n у файлах властивостей, але я не думаю, що це є великим рішенням у вашому випадку.


Значення у файлі властивостей може охоплювати декілька рядків: Просто закінчіть всі рядки, однак останній, з нахилом. Це все ж залишає проблему того, що ви використовуєте як роздільник рядків, оскільки це залежить від платформи. Я припускаю, що ви можете скористатися простим \ n, а потім у своєму коді, прочитавши властивість, виконати пошук і заміну \ n на рядок line.separator. Це здається трохи хитрую, але, мабуть, ви можете написати функцію, яка вилучає властивість і одночасно виконує цю маніпуляцію. Ну, все, що передбачає, що ви б писали ці рядки у файл, що є великим припущенням.
Джей

3

Я знаю, що це старе питання, однак для зацікавлених розробників Багаторядкові літерали будуть в # Java12

http://mail.openjdk.java.net/pipermail/amber-dev/2018-July/003254.html


ніпе. але зробив , нарешті , зробити його для Java 13. Які мої команди, швидше за все , прийняти приблизно півтора десятиліття.
javadba

3

Я пропоную скористатись утилітою, запропонованою ThomasP, а потім зв’язати її у процесі збирання. Зовнішній файл все ще присутній, щоб містити текст, але файл не читається під час виконання. Тоді робочий процес:

  1. Побудуйте утиліту «текстовий файл до коду Java» та перевірте в контролі версій
  2. У кожній збірці запустіть утиліту проти файлу ресурсу, щоб створити переглянутий джерело Java
  3. Джерело Java містить заголовок, як і class TextBlock {...далі статичний рядок, який автоматично генерується з файлу ресурсу
  4. Створіть створений файл Java з рештою коду

2

Коли використовується довга серія +, створюється лише один StringBuilder, якщо тільки String не визначений під час компіляції, в цьому випадку StringBuilder не використовується!

Єдиний раз, коли StringBuilder є більш ефективним - це коли для побудови рядка використовуються кілька операторів.

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

Примітка: Створено лише один 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

Щоб уточнити своє питання далі, мене взагалі не хвилює ефективність. Мене хвилюють проблеми з технічним обслуговуванням та дизайном.

Зробіть це максимально зрозумілим і простим.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.