设置Java VM line.separator

14 java properties linefeed

有没有人找到一种方法如何line.separator在VM启动时指定Java 属性?我在考虑这样的事情:

java -Dline.separator="\n"
Run Code Online (Sandbox Code Playgroud)

但是这并没有将"\n"解释为换行符.有任何想法吗?

aio*_*obe 16

尝试使用java -Dline.separator=$'\n'.应该这样做,至少在bash中.

这是一个测试运行:

aioobe@r60:~/tmp$ cat Test.java 
public class Test {
    public static void main(String[] args) {
        System.out.println("\"" + System.getProperty("line.separator") + "\"");
    }
}
aioobe@r60:~/tmp$ javac Test.java && java -Dline.separator=$'\n' Test
"
"
aioobe@r60:~/tmp$ 
Run Code Online (Sandbox Code Playgroud)

注意:

该表达式$''使用Bash功能ANSI-C Quoting.它会扩展反斜杠转义字符,从而$'\n'生成换行符(ASCII代码10)字符,用单引号括起来.请参阅Bash手册,第3.1.2.4ANSI-C引用.

  • 我可以肯定地看到它在例如测试场景中是如何有用的. (4认同)

dim*_*414 5

为了弥合 aioobe 和 Bozho 的答案之间的差距,我还建议不要line.separator在 JVM 启动时设置参数,因为这可能会破坏 JVM 和库代码对运行环境做出的许多基本假设。例如,如果您依赖某个库on 依赖line.separator于以跨平台方式存储配置文件,您刚刚破坏了这种行为。是的,这是一个边缘情况,但这使得它变得更加邪恶,几年后问题确实出现了,现在你所有的代码都依赖于这个调整到位,而你的库(正确)假设它不是。

也就是说,有时这些事情是您无法控制的,例如当一个库依赖于line.separator并且无法让您显式覆盖该行为时。在这种情况下,您无法覆盖该值,或者更痛苦的事情,例如手动重新实现或修补代码。

对于那些有限的情况,覆盖是可以接受的line.separator,但我们必须遵循两个规则:

  1. 最小化覆盖范围
  2. 无论如何恢复覆盖

AutoCloseabletry-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);
  }
}
Run Code Online (Sandbox Code Playgroud)

我的用例是 with Files.write(),这是一种非常方便的方法,除了它明确依赖于line.separator. 通过包装对Files.write()我的调用,我可以清楚地指定我想要使用的行分隔符,而不必冒险将其暴露给我的应用程序的任何其他部分(当然请注意,这仍然不是线程安全的)。

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