user346034
user346034

Reputation:

Setting Java VM line.separator

Has anybody found a way how to specify the Java line.separator property on VM startup? I was thinking of something like this:

java -Dline.separator="\n"

But this doesn't interprete the "\n" as linefeed character. Any ideas?

Upvotes: 15

Views: 17757

Answers (4)

DuncG
DuncG

Reputation: 15126

For Windows users it isn't easy to switch line separator with java -Dline.separator=\n as as Windows CMD mangles the property value, but you can use java @argfile instead.

Just open Windows CMD.EXE to create a config file for the OS style line separator property:

echo -Dline.separator="\n"   > gnu.linesep.args
echo -Dline.separator="\r\n" > windows.linesep.args

Then you can invoke your application on Windows with the line separator behaviour of GNU/Linux:

java @gnu.linesep.args ... etc

You can also make GNU/Linux launch behave with Windows line separator using file:

java @windows.linesep.args ... etc
# which is same as the direct approach mentioned by aioobe:
java -Dline.separator=$'\r\n' ... etc

See the example test in this answer How can I force Java stdout on windows to use the "\n" line seperator?

Upvotes: 0

dimo414
dimo414

Reputation: 48804

To bridge the gap between aioobe and Bozho's answers, I also would advise against setting the line.separator parameter at JVM startup, as this potentially breaks many fundamental assumptions the JVM and library code makes about the environment being run in. For instance, if a library you depend on relies on line.separator in order to store a config file in a cross-platform way, you've just broken that behavior. Yes, it's an edge case, but that makes it all the more nefarious when, years from now, a problem does crop up, and now all your code is dependent on this tweak being in place, while your libraries are (correctly) assuming it isn't.

That said, sometimes these things are out of your control, like when a library relies on line.separator and provides no way for you to override that behavior explicitly. In such a case, you're stuck overriding the value, or something more painful like re-implementing or patching the code manually.

For those limited cases, the it's acceptable to override line.separator, but we've got to follow two rules:

  1. Minimize the scope of the override
  2. Revert the override no matter what

Both of these requirements are well served by AutoCloseable and the try-with-resources syntax, so I've implemented a PropertiesModifier class that cleanly provides both.

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

My use case was with Files.write(), which is a very convenient method, except it explicitly relies on line.separator. By wrapping the call to Files.write() I can cleanly specify the line separator I want to use, without risking exposing this to any other parts of my application (take note of course, that this still isn't thread-safe).

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

Upvotes: 6

aioobe
aioobe

Reputation: 420951

Try using java -Dline.separator=$'\n'. That should do the trick, at least in bash.

Here is a test-run:

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$ 

Note:

The expression $'' uses the Bash feature ANSI-C Quoting. It expands backslash-escaped characters, thus $'\n' produces a line feed (ASCII code 10) character, enclosed in single quotes. See Bash manual, section 3.1.2.4 ANSI-C Quoting.

Upvotes: 17

Bozho
Bozho

Reputation: 597076

I wouldn't do that if I were you. The line-separator is platform specific, and should remain so. If you want to write windows-only or linux-only files, define a UNIX_LINE_SEPARATOR constant somewhere and use it instead.

Upvotes: 2

Related Questions