Установка Java VM line.separator

Кто-нибудь нашел способ указать свойство Java line.separator при запуске VM? Я думал о чем-то вроде этого:

java -Dline.separator="\n"

Но это не интерпретирует символ "\n" как символ перевода строки. Любые идеи?

Ответы

Ответ 1

Попробуйте использовать java -Dline.separator=$'\n'. Это должно сделать трюк, по крайней мере, в bash.

Вот тест:

[email protected]:~/tmp$ cat Test.java 
public class Test {
    public static void main(String[] args) {
        System.out.println("\"" + System.getProperty("line.separator") + "\"");
    }
}
[email protected]:~/tmp$ javac Test.java && java -Dline.separator=$'\n' Test
"
"
[email protected]:~/tmp$ 

Замечания:

Выражение $'' использует функцию Bash ANSI-C Quoting. Он расширяет обратные символы с обратным слэшем, поэтому $'\n' создает символ строки (ASCII-код 10), заключенный в одинарные кавычки. См. Руководство пользователя Bash, раздел 3.1.2.4. Котировка ANSI-C.

Ответ 2

Чтобы устранить разрыв между ответами aioobe и Bozho, я также советовал бы не устанавливать параметр line.separator при запуске JVM, так как это потенциально нарушает многие основополагающие предположения, которые JVM и код библиотеки делают для среды, в которой выполняется. Например, если библиотека, на которую вы полагаетесь, полагается на line.separator, чтобы сохранить конфигурационный файл кросс-платформенным способом, вы просто нарушили это поведение. Да, это крайний случай, но это делает его тем более гнусным, когда через несколько лет возникает проблема, и теперь весь ваш код зависит от этой настройки на месте, в то время как ваши библиотеки (правильно) предполагают это нет.

Тем не менее, иногда эти вещи находятся вне вашего контроля, например, когда библиотека полагается на line.separator и не дает вам возможности явно переопределить это поведение. В таком случае вы застряли, переопределяя значение, или что-то более болезненное, как повторное внедрение или исправление кода вручную.

В этих ограниченных случаях допустимо переопределить line.separator, но мы должны следовать двум правилам:

  • Минимизировать область переопределения
  • Отменить переопределение независимо от того, что

Оба эти требования хорошо обслуживаются AutoCloseable и try-with-resources, поэтому я внедрил класс PropertiesModifier, который обеспечивает чистоту.

/**
 * Class which enables temporary modifications to the System properties,
 * via an AutoCloseable.  Wrap the behavior that needs your modification
 * in a try-with-resources block in order to have your properties
 * apply only to code within that block.  Generally, alternatives
 * such as explicitly passing in the value you need, rather than pulling
 * it from System.getProperties(), should be preferred to using this class.
 */
public class PropertiesModifier  implements AutoCloseable {
  private final String original;

  public PropertiesModifier(String key, String value) {
    this(ImmutableMap.of(key, value));
  }

  public PropertiesModifier(Map<String, String> map) {
    StringWriter sw = new StringWriter();
    try {
      System.getProperties().store(sw, "");
    } catch (IOException e) {
      throw new AssertionError("Impossible with StringWriter", e);
    }
    original = sw.toString();
    for(Map.Entry<String, String> e : map.entrySet()) {
      System.setProperty(e.getKey(), e.getValue());
    }
  }

  @Override
  public void close() {
    Properties set = new Properties();
    try {
      set.load(new StringReader(original));
    } catch (IOException e) {
      throw new AssertionError("Impossible with StringWriter", e);
    }
    System.setProperties(set);
  }
}

Мой вариант использования был Files.write(), что является очень удобным методом, за исключением того, что он явно использует line.separator. Обернув вызов Files.write(), я могу четко указать разделитель строк, который я хочу использовать, не рискуя разоблачить это для других частей моего приложения (обратите внимание, конечно, что это все еще не является потокобезопасным).

try(PropertiesModifier pm = new PropertiesModifier("line.separator", "\n")) {
  Files.write(file, ImmutableList.of(line), Charsets.UTF_8);
}

Ответ 3

Я бы не сделал этого, если бы был вами. Разделитель строк является специфичным для платформы и должен оставаться таким. Если вы хотите писать файлы только для Windows или только для Linux, определите константу UNIX_LINE_SEPARATOR где-нибудь и используйте ее вместо этого.