【问题标题】:Setting Java VM line.separator设置 Java VM line.separator
【发布时间】:2011-04-12 03:51:02
【问题描述】:

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

java -Dline.separator="\n"

但这不会将“\n”解释为换行符。有什么想法吗?

【问题讨论】:

    标签: java properties linefeed


    【解决方案1】:

    尝试使用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$ 
    

    注意:

    表达式 $'' 使用 Bash 功能ANSI-C 引用。它扩展了反斜杠转义字符,因此$'\n' 生成一个换行符(ASCII 代码 10),用单引号括起来。请参阅 Bash 手册,3.1.2.4 ANSI-C Quoting 部分。

    【讨论】:

    • 我绝对可以看到它在测试场景中的用途。
    • 谢谢,从命令行解决了它。它仍然不能在 Eclipse IDE 的运行配置中工作,但这并不重要。
    【解决方案2】:

    为了弥合 aioobe 和 Bozho 的答案之间的差距,我还建议不要在 JVM 启动时设置 line.separator 参数,因为这可能会破坏 JVM 和库代码对正在运行的环境所做的许多基本假设。例如,如果您依赖的库依赖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);
      }
    }
    

    我的用例是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 常量并改用它。

      【讨论】:

      • 完全正确。有些东西不应该改变。任何依赖 line.separator 的具有特定值的 api 都已损坏
      • 如果你认为其他人不仅愚蠢而且有他们的理由,我会很感激。我的是,从 shell 脚本中调用 Java 程序。 shell 脚本使用 Java 应用程序的标准输出并进一步处理它。如果从 Cygwin 运行该脚本,Java 将使用 Windows 换行符。但是,这会导致脚本出现问题。因此,我想更改 Java 的默认 line.separator,以防它从 Cygwin 运行。
      • 首先,您可以在问题中分享这些详细信息。其次,那个不能用 Java 编程的 shell 脚本有什么特别之处?拥有一个你喜欢用 Cygwin 运行的纯 linux 的 Java 软件对我来说听起来并不合理。要么提供.bat 文件(例如Tomcat),要么将功能移至Java 类。最后,我不认为任何人都是愚蠢的。但人们往往缺乏经验。
      • 很公平。我只是在这里支持一个处理一些遗留软件的共同开发者。因此,我的决定并不多。我只是Java问题的人。对于复杂的 Java 问题,你们是我的伙伴。 ;-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-18
      • 2011-08-25
      相关资源
      最近更新 更多