【发布时间】:2009-06-18 18:08:29
【问题描述】:
当然是在 Java 中。我正在编写一个程序并在 Windows 环境下运行它,但我需要以 Unix 格式完成输出(.csv)。有什么简单的解决办法吗?谢谢!
【问题讨论】:
-
unix 格式是什么意思?行尾格式?
当然是在 Java 中。我正在编写一个程序并在 Windows 环境下运行它,但我需要以 Unix 格式完成输出(.csv)。有什么简单的解决办法吗?谢谢!
【问题讨论】:
要编写带有 unix 行结尾的文件,请在派生自 PrintWriter 的类中覆盖 println,并将 print 与 \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();
这避免了必须触及代码中任何现有的 println 调用。
【讨论】:
println()。所有其他 println 方法(例如 println(String)、println(int) 等都记录为打印数据然后调用 println()。此外,您应该在受保护的变量 lock 上进行同步。
write 的调用处理锁。
PrintWriter有效,但对于PrintStream不。
注意:正如 cmets 中所报告的,下面给出的方法在 JDK 9+ 中中断。使用 James H. 的回答中的方法。
“Unix 格式”是指使用“\n”作为行终止符而不是“\r\n”吗?只需在创建 PrintWriter 之前设置 line.separator 系统属性即可。
仅作为演示:
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();
}
}
当然,这为整个 JVM 设置了它,这并不理想,但它可能就是你所需要的。
【讨论】:
PrintWriter,甚至可能会影响面向行的阅读器。如果您只希望特定的 PrintWriter 实例输出 unix 格式,您应该考虑其他选项。
假设您提到的格式问题是 Windows 换行符是回车换行符 ("\r\n") 而 Unix 换行符只是换行符 ("\n"),这是确保文件使用 LF 和不是 CRLF 是避开 println 而是使用 print("\n") 来终止行。
所以而不是:
writer.println("foo,bar,88");
使用
writer.print("foo,bar,88\n");
您可以在相关文件中搜索println 以确保您能全部找到。
【讨论】:
我看到另外 2 个选项不会影响整个系统或导致并发问题,例如设置 lineSeparator。我还会声明一个表示行尾的枚举。
enum LineEnding {
UNIX("\n"), DOS("\r\n");
private String lineSeparator;
LineEnding(String lineSeparator) {
this.lineSeparator = lineSeparator;
}
public String getLineSeparator() {
return lineSeparator;
}
}
使用反射设置lineSeperator。
您应该创建一个封装访问的工厂,使用 反射以向客户端隐藏 PrintWriter 内部。例如。客户端代码应如下所示:
PrintWriterFactory.newPrintWriter(someWriter, LineEnding.UNIX);
虽然实现可能如下所示:
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:工厂不能是静态的。如果 PrintWriter 实现从一个 JDK 更改为另一个,您可以使用一个接口和多个实现,因此您必须使用另一种反射策略。
扩展 PrintWriter 并覆盖 println() 方法
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();
}
}
}
【讨论】:
一个偏执的程序员会在系统属性对象上进行同步,至少在不同的 Printwriters 需要不同类型的行终止符时是这样。
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);
}
}
}
}
【讨论】:
Properties.setProperty() 是同步的,所以如果线程持有属性锁,其他人应该无法更改它。