20

我一直对 Java 中不同 IO 实现的数量感到有些困惑,现在我完全陷入了我的项目开发中,同时我也在花时间阅读有用的东西。

我已经意识到该类的不同子类之间没有对新手友好的比较(除了API for Writer 类Writer的简短解释) 。所以我想我会提出这个问题,这些不同的子类有什么用?

例如,我通常使用FileWriter带有 a 的包装BufferedWriter输出到文件,但我一直对没有println()类似方法的事实感到恼火,并且必须newLine()每隔一行使用一次(以使输出可读)。PrintWriterprintln()方法,但没有支持追加的构造函数......

如果您能从您的经验中给我两分钱,或者您可能偶然发现的一个不错的指南/操作方法,我将非常感激。

编辑:感谢大家的回复,我非常感谢这里传递的信息。有点不幸的是,整个append()事情最终都成为焦点,这只是一个例子。我的问题主要是指所有不同实现的需要和使用,我想在几个答案中有所提及。

很难选择一个答案被接受,因为有三个非常可靠的答案,每个答案都有助于我对问题的理解。我将不得不和 Anon 一起去,因为他的代表次数最少。点(我想他是新来的)。他有 15 个答案,其中一些非常好,提出了 0 个问题。我会说很好的贡献,这值得推广。

话虽如此,ColinD 和 Jay 也提供了非常好的答案,并指出了有趣的想法。尤其是 Jay 关于 Java 自动包装 a 的评论BufferedWriter值得注意。再次感谢大家,真的很感激!

4

4 回答 4

9

这些java.io类通常遵循装饰器模式。因此,虽然PrintWriter没有您可能想要的特定构造函数,但它确实有一个带有另一个的构造函数Writer,因此您可以执行以下操作:

FileOutputStream fos = null;
try
{
    fos = new FileOutputStream("foo.txt");
    PrintWriter out = new PrintWriter(
                          new BufferedWriter(
                              new OutputStreamWriter(fos, "UTF-8")));
    // do what you want to do
    out.flush();
    out.close();
}
finally
{
    // quietly close the FileOutputStream (see Jakarta Commons IOUtils)
}

作为一般用法说明,您总是希望将低级 Writer(例如FileWriterOutputStreamWriter)包装在 a 中BufferedWriter,以最小化实际的 IO 操作。但是,这意味着您需要显式刷新和关闭最外层的 Writer,以确保写入所有内容。

然后你需要在一个块中关闭低级别的Writer finally,以确保你不泄漏资源。

编辑

看了MForster的回答,我又看了看 FileWriter 的 API。而且我意识到它不需要明确的字符集,这是一件非常糟糕的事情。因此,我编辑了我的代码片段以使用由带有显式字符集的FileOutputStreaman 包装的。OutputStreamWriter

于 2010-11-01T14:29:47.260 回答
7

FileWriter通常是不可接受的使用类。它不允许您指定Charset用于写入的字符集,这意味着您会被所运行平台的默认字符集所困扰。不用说,这使得无法一致地使用相同的字符集来读取和写入文本文件,并可能导致数据损坏。

而不是使用FileWriter,您应该将 a 包装FileOutputStream在一个OutputStreamWriter. OutputStreamWriter确实允许您指定一个字符集:

File file = ...
OutputStream fileOut = new FileOutputStream(file);
Writer writer = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8"));

PrintWriter与上述一起使用,只需将其包装BufferedWriter在 a 中PrintWriter

PrintWriter printWriter = new PrintWriter(writer);

可以只使用带有 a和字符集名称的PrintWriter构造函数:File

PrintWriter printWriter = new PrintWriter(file, "UTF-8");

这适用于您的特定情况,实际上与上面的代码完全相同,但最好知道如何通过包装各个部分来构建它。

其他Writer类型主要用于特殊用途:

  • StringWriter只是一个Writer可以用来创建一个String. CharArrayWriter是一样的char[]
  • PipedWriter用于管道到PipedReader.

编辑:

我注意到您评论了另一个关于以这种方式创建作家的冗长性的答案。请注意,有一些库(如Guava )有助于减少常见操作的冗长性。例如,将 a 写入String特定字符集中的文件。使用番石榴,你可以写:

Files.write(text, file, Charsets.UTF_8);

你也可以创建一个BufferedWriter这样的:

BufferedWriter writer = Files.newWriter(file, Charsets.UTF_8);
于 2010-11-01T14:47:12.230 回答
4

PrintWriter 没有采用“附加”参数的构造函数,但 FileWriter 有。对我来说,这似乎是合乎逻辑的。PrintWriter 不知道您是否正在写入文件、套接字、控制台、字符串等。在写入套接字时“附加”是什么意思?

所以做你想做的事情的正确方法很简单:

PrintWriter out=new PrintWriter(new BufferedWriter(new FileWriter(myfile, append)));

有趣的旁注:如果将 OutputStream 包装在 PrintWriter 中,Java 会自动在中间插入 BufferedWriter。但是,如果您将 Writer 包装在 PrintWriter 中,则不会。所以说:

PrintWriter out=new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(myfile))));

只需不要使用 BufferedWriter 和 OutputStreamWriter,无论如何您都可以免费获得它们。我不知道这种不一致是否有充分的理由。

确实,您不能像 ColinD 注释那样在 FileWriter 中指定字符编码。我不知道这使它“不可接受”。我几乎总是非常乐意接受默认编码。也许如果您使用的是英语以外的语言,这是一个问题。

当我第一次开始使用 Java 时,需要在层中包装 Writer 或 OutputStream 让我感到困惑。但是一旦你掌握了窍门,这没什么大不了的。你只需要将你的思想投入到编写框架中。每个作家都有一个功能。想一想,我想打印到一个文件,所以我需要将一个 FileWriter 包装在一个 PrintWriter 中。或者,我想将输出流转换为写入器,所以我需要一个 OutputStreamWriter。等等。

或者,也许你只是习惯了你一直使用的那些。弄清楚一次并记住你是如何做到的。

于 2010-11-01T15:43:50.713 回答
2

您可以像这样创建一个附加PrintWriter

OutputStream os = new FileOutputStream("/tmp/out", true);
PrintWriter writer = new PrintWriter(os);

编辑: Anon 的帖子关于在两者BufferedWriter之间使用 a 和指定编码都是正确的。

于 2010-11-01T14:31:46.333 回答