[ACCEPTED]-Is there a way to make PrintWriter output to UNIX format?-unix

Accepted answer
Score: 18

To write a file with unix line endings, override 4 println in a class derived from PrintWriter, and 3 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 2 touch any existing println calls you have 1 in your code.

Score: 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 5 the line terminator instead of "\r\n"? Just 4 set the line.separator system property before you create 3 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 2 that sets it for the whole JVM, which isn't 1 ideal, but it may be all you happen to need.

Score: 3

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

So 2 instead of:

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

use

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

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

Score: 3

I see 2 more options that do not affect 12 the whole system or lead to concurrency 11 proplems like setting the lineSeparator. I would also 10 declare an enum that represents the line 9 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 8 encapsulates the access using reflection 7 to hide PrintWriter internals from clients. E.g. client 6 code should look like this:

    PrintWriterFactory.newPrintWriter(someWriter, LineEnding.UNIX);
    

    While the implementation 5 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 4 not be static. You can use an interface 3 and multiple implementations if the PrintWriter implementation 2 changes from one JDK to another and thus 1 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();
        }
      }
    }
    
Score: 2

A paranoid programmer would synchronize 3 on the system properties object, at least 2 if different Printwriters needs different 1 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);
            }
        }
    }
}

More Related questions