12

In Java, of course. I'm writing a program and running it under a Windows environment, but I need the output (.csv) to be done in Unix format. Any easy solution? Thanks!

Monster
  • 1,573
  • 6
  • 23
  • 35

5 Answers5

20

To write a file with unix line endings, override println in a class derived from PrintWriter, and use print with \n.

PrintWriter out = new PrintWriter("testFile") {
    @Override
    public void println() {
        write('\n');
    }
};
out.println("This file will always have unix line endings");
out.println(41);
out.close();

This avoids having to touch any existing println calls you have in your code.

James H.
  • 369
  • 2
  • 5
  • No, you should just override `println()`. All the other println methods (e.g. `println(String)`, `println(int)`, etc. are documented as printing the data and then calling `println()`. Also, you should synchronize on the protected variable `lock`. – Klitos Kyriacou Dec 13 '16 at 14:33
  • Fair point, overriding println() is a better idea. I have updated my code. The call to `write` handles the lock. – James H. Jan 09 '17 at 18:04
  • In case anyone else falls in the same trap as me, the above does work for `PrintWriter` but _not_ for `PrintStream`. – Mumrah May 10 '19 at 11:04
12

Note: as reported in comments, the approach given below breaks in JDK 9+. Use the approach in James H.'s answer.


By "Unix format" do you mean using "\n" as the line terminator instead of "\r\n"? Just set the line.separator system property before you create the PrintWriter.

Just as a demo:

import java.io.*;

public class Test
{
    public static void main(String[] args)
        throws Exception // Just for simplicity
    {
        System.setProperty("line.separator", "xxx");
        PrintWriter pw = new PrintWriter(System.out);
        pw.println("foo");
        pw.println("bar");
        pw.flush();
    }
}

Of course that sets it for the whole JVM, which isn't ideal, but it may be all you happen to need.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    This breaks on JDK9+. [James H.`s answer](https://stackoverflow.com/a/14749004/2977136) is the preferred way. See http://mail.openjdk.java.net/pipermail/core-libs-dev/2015-January/030567.html – Abex Aug 09 '18 at 04:37
  • @Abex: Thanks for reporting that. I've added a note to the top of the answer. I wish a simpler way of doing this had been devised than subclassing, but it's better than nothing. – Jon Skeet Aug 09 '18 at 11:55
  • Remember that the system property will affect every `PrintWriter` within the JVM and maybe even affect line oriented readers. If you only want a specific `PrintWriter` instance to output unix format you should consider other options. – René Link Jul 02 '20 at 16:20
  • @RenéLink: I did specifically say "Of course that sets it for the whole JVM, which isn't ideal" – Jon Skeet Jul 02 '20 at 16:36
  • @JonSkeet No, problem. I just want to mention it to prevent someone to do this e.g. in a servlet request ;) – René Link Jul 03 '20 at 07:56
3

Assuming the formatting issue you refer to is that Windows line breaks are Carriage Return-Line Feed ("\r\n") while Unix ones are Line Feed ("\n") only, the easiest way to make sure your file uses LF and not CRLF is to eschew println and instead use print("\n") to terminate lines.

So instead of:

writer.println("foo,bar,88");

use

writer.print("foo,bar,88\n");

You can just search the relevant files for println to make sure you catch them all.

Zarkonnen
  • 22,200
  • 14
  • 65
  • 81
  • The difference between this approach and Jon Skeet's is that his is global, but requires fewer changes. As long as you're sure you don't care that everything in your program will be using \n instead or \r\n, just setting line.separator is probably easier. – Zarkonnen Jun 18 '09 at 18:15
  • Ah, I see. Excellent idea also. In my case though (although I should have specified), I'm looking for a global approach. Thanks! – Monster Jun 18 '09 at 18:18
3

I see 2 more options that do not affect the whole system or lead to concurrency proplems like setting the lineSeparator. I would also declare an enum that represents the line endings.

 enum LineEnding {
  UNIX("\n"), DOS("\r\n");

  private String lineSeparator;

  LineEnding(String lineSeparator) {
    this.lineSeparator = lineSeparator;
  }

  public String getLineSeparator() {
    return lineSeparator;
  }
}
  1. Set the lineSeperator using reflection.

    You should create a factory that encapsulates the access using reflection to hide PrintWriter internals from clients. E.g. client code should look like this:

    PrintWriterFactory.newPrintWriter(someWriter, LineEnding.UNIX);
    

    While the implementation could look like this:

    public class PrintWriterFactory {
    
      private static final Field lineSeparatorField;
    
      static {
        try {
          lineSeparatorField = PrintWriter.class.getDeclaredField("lineSeparator");
          lineSeparatorField.setAccessible(true);
        } catch (NoSuchFieldException e) {
          throw new IllegalStateException("java.io.PrintWriter implementation changed. Unable to determine lineSeparator field.", e);
        }
      }
    
      public static PrintWriter newPrintWriter(Writer writer, LineEnding lineEnding) {
        PrintWriter printWriter = new PrintWriter(writer);
    
        try {
          lineSeparatorField.set(printWriter, lineEnding.getLineSeparator());
        } catch (IllegalAccessException e) {
          throw new IllegalStateException("Can't set line ending", e);
        }
    
        return printWriter;
      }
    }
    

    PS: The factory must not be static. You can use an interface and multiple implementations if the PrintWriter implementation changes from one JDK to another and thus you must use another reflection strategy.

  2. Extend PrintWriter and overwrite the println() method

    public class LineEndingPrintWriter extends PrintWriter {
    
      protected boolean autoFlush = false;
      private LineEnding lineEnding;
    
      public LineEndingPrintWriter(Writer out, LineEnding lineEnding) {
        this(out, false, lineEnding);
      }
    
      public LineEndingPrintWriter(Writer out, boolean autoFlush, LineEnding lineEnding) {
        super(out, autoFlush);
        this.autoFlush = autoFlush;
        this.lineEnding = lineEnding;
      }
    
      public LineEndingPrintWriter(OutputStream out, LineEnding lineEnding) {
        this(out, false, lineEnding);
      }
    
      public LineEndingPrintWriter(OutputStream out, boolean autoFlush, LineEnding lineEnding) {
        super(out, autoFlush);
        this.autoFlush = autoFlush;
        this.lineEnding = lineEnding;
      }
    
      protected void ensureOpen() throws IOException {
        if (out == null)
          throw new IOException("Stream closed");
      }
    
      public void println() {
        // Method body taken from java.io.PrintWriter.println();
        try {
          synchronized (lock) {
            ensureOpen();
    
            out.write(lineEnding.getLineSeparator());
    
            if (autoFlush) {
              out.flush();
            }
          }
        } catch (InterruptedIOException e) {
          Thread.currentThread().interrupt();
        } catch (IOException e) {
          setError();
        }
      }
    }
    
René Link
  • 48,224
  • 13
  • 108
  • 140
2

A paranoid programmer would synchronize on the system properties object, at least if different Printwriters needs different types of line terminators.

public static PrintWriter createPrintWriter(OutputStreamWriter out, boolean autoflush){
    Properties props = System.getProperties();
    synchronized (props) {
        Object old = null;
        try {
        old = props.setProperty("line.separator", "\n");
        return  new PrintWriter(out, autoflush);
        } finally {
            if( old != null ) {
                props.put("line.separator", old);
            }
        }
    }
}
KarlP
  • 5,149
  • 2
  • 28
  • 41
  • What is the reasoning here :) ? – James P. Oct 09 '12 at 08:19
  • 2
    To make sure that you actually will get the property you just did set, unless you are running in one thread, or always will use the same line terminator, and that is never going to change. – KarlP Dec 15 '13 at 19:56
  • Unfortunately this doesn't prevent another method simultaneously changing the property without using this lock. – Klitos Kyriacou Dec 13 '16 at 14:27
  • Actually, `Properties.setProperty()` is synchronized, so if the thread is holding a lock on Properties, no one else should be able to change it. – KarlP Dec 15 '16 at 22:24