使用 FileWriter (Java) 以 UTF-8 格式写入文件?

Posted

技术标签:

【中文标题】使用 FileWriter (Java) 以 UTF-8 格式写入文件?【英文标题】:Write a file in UTF-8 using FileWriter (Java)? 【发布时间】:2012-04-08 19:49:56 【问题描述】:

但是,我有以下代码,我希望它写为 UTF-8 文件来处理外来字符。有没有办法做到这一点,是否需要有一个参数?

非常感谢您对此提供的帮助。谢谢。

try 
  BufferedReader reader = new BufferedReader(new FileReader("C:/Users/Jess/My Documents/actresses.list"));
  writer = new BufferedWriter(new FileWriter("C:/Users/Jess/My Documents/actressesFormatted.csv"));
  while( (line = reader.readLine()) != null) 
    //If the line starts with a tab then we just want to add a movie
    //using the current actor's name.
    if(line.length() == 0)
      continue;
    else if(line.charAt(0) == '\t') 
      readMovieLine2(0, line, surname.toString(), forename.toString());
     //Else we've reached a new actor
    else 
      readActorName(line);
    
  
 catch (IOException e) 
  e.printStackTrace();

【问题讨论】:

【参考方案1】:

安全编码构造函数

让 Java 正确地通知您编码错误是很棘手的。对于InputStreamReaderOutputStreamWriter 中的每一个,您必须使用四个备用构造函数中最冗长的 以及最少使用收到有关编码故障的适当异常。

对于文件 I/O,始终确保始终将花哨的编码器参数用作 OutputStreamWriterInputStreamReader 的第二个参数:

  Charset.forName("UTF-8").newEncoder()

还有其他更奇特的可能性,但三种更简单的可能性都不适用于异常处理。这些可以:

 OutputStreamWriter char_output = new OutputStreamWriter(
     new FileOutputStream("some_output.utf8"),
     Charset.forName("UTF-8").newEncoder() 
 );

 InputStreamReader char_input = new InputStreamReader(
     new FileInputStream("some_input.utf8"),
     Charset.forName("UTF-8").newDecoder() 
 );

至于运行

 $ java -Dfile.encoding=utf8 SomeTrulyRemarkablyLongcLassNameGoeShere

问题在于,它不会对字符流使用完整的编码器参数形式,因此您将再次错过编码问题。

更长的例子

这是一个更长的例子,这个例子管理一个进程而不是一个文件,我们将两个不同的输入字节流和一个输出字节流全部提升为 UTF-8 字符流具有完整的异常处理

 // this runs a perl script with UTF-8 STDIN,OUT,ERR streams
 Process
 slave_process = Runtime.getRuntime().exec("perl -CS script args");

 // fetch his stdin byte stream...
 OutputStream
 __bytes_into_his_stdin  = slave_process.getOutputStream();

 // and make a character stream with exceptions on encoding errors
 OutputStreamWriter
   chars_into_his_stdin  = new OutputStreamWriter(
                             __bytes_into_his_stdin,
         /* DO NOT OMIT! */  Charset.forName("UTF-8").newEncoder()
                         );

 // fetch his stdout byte stream...
 InputStream
 __bytes_from_his_stdout = slave_process.getInputStream();

 // and make a character stream with exceptions on encoding errors
 InputStreamReader
   chars_from_his_stdout = new InputStreamReader(
                             __bytes_from_his_stdout,
         /* DO NOT OMIT! */  Charset.forName("UTF-8").newDecoder()
                         );

// fetch his stderr byte stream...
 InputStream
 __bytes_from_his_stderr = slave_process.getErrorStream();

 // and make a character stream with exceptions on encoding errors
 InputStreamReader
   chars_from_his_stderr = new InputStreamReader(
                             __bytes_from_his_stderr,
         /* DO NOT OMIT! */  Charset.forName("UTF-8").newDecoder()
                         );

现在您有了三个在编码错误时都会引发异常的字符流,分别称为chars_into_his_stdinchars_from_his_stdoutchars_from_his_stderr

这仅比您解决问题所需的复杂一些,我在此答案的前半部分给出了解决方案。关键是这是检测编码错误的唯一方法。

请不要让我开始谈论 PrintStreams 饮食异常。

【讨论】:

很好的答案,但我认为它有一个小错误 -InputStreamReader char_input = new InputStreamWriter 应该读作:InputStreamReader char_input = new InputStreamReader InputStreamReader 构造函数采用CharsetDecoder,而不是CharsetEncoder 但这是一个真正的问题吗,UTF-8 无法表示什么,我认为它可以编码任何东西。 如果您想抱怨 Streams 吃异常,请尝试 CipherInputStream,它会删除 BadPaddingException,即使它们是由经过身份验证的密码流创建的 :( 我在您的代码中发现了一个小错误:"Charset.forName("UTF-8").newEncoder()" for "InputStreamReader" 应该是 "Charset.forName("UTF-8") .newDecoder()”。所以“解码器”而不是“编码器”。但无论如何,感谢这个不错的答案和+1。 :) (整个 Java IO 系统一直都是一团糟。应该像 Joda Time 重做日期一样彻底重做。)【参考方案2】:

抛弃FileWriterFileReader,它们完全没用,因为它们不允许您指定编码。相反,使用

new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)

new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);

【讨论】:

如果您不使用非常冗长的Charset.forName("UTF-8").newDecoder() 参数(或一些更高级的构造)而不仅仅是"UTF-8",您将不会收到正确的编码错误通知(阅读:异常将是被抑制,它会神秘地隐藏编码错误)。 new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8 )【参考方案3】:

您需要使用OutputStreamWriter 类作为BufferedWriter 的写入器参数。它确实接受编码。为它查看javadocs。

有点像这样:

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
    new FileOutputStream("jedis.txt"), "UTF-8"
));

或者您可以使用系统属性file.encoding 将当前系统编码设置为UTF-8。

java -Dfile.encoding=UTF-8 com.jediacademy.Runner arg1 arg2 ...

如果您只需要此特定文件,您也可以在运行时使用System.setProperty(...) 将其设置为系统属性,但在这种情况下,我认为我更喜欢OutputStreamWriter

通过设置系统属性,您可以使用FileWriter 并期望它将使用 UTF-8 作为文件的默认编码。在这种情况下,适用于您读取和写入的所有文件。

编辑

从 API 19 开始,您可以将字符串“UTF-8”替换为 StandardCharsets.UTF_8

正如tchrist 在下面的 cmets 中所建议的那样,如果您打算检测文件中的编码错误,您将不得不使用OutputStreamWriter 方法并使用接收字符集编码器的构造函数。

有点像

CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
encoder.onMalformedInput(CodingErrorAction.REPORT);
encoder.onUnmappableCharacter(CodingErrorAction.REPORT);
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("jedis.txt"),encoder));

您可以选择操作IGNORE | REPLACE | REPORT

另外,这个问题已经回答了here。

【讨论】:

这还不够。你还需要一个InputStreamReader(InputStream in, CharsetDecoder dec),这样最后一个参数就是Charset.forName("UTF-8").newDecoder() 如果您这样做,输入编码错误将被静默删除。 不需要编码器。构造函数在输入/输出类中都接受字符串、字符集或编码器。不知道你的评论是什么意思。请详细说明一下好吗? @edalorzo 如果您在错误数据上测试四个不同的In,OutputStreamReader,Writer 构造函数,您会发现其中三个屏蔽所有异常应该 i> 由编码错误引起,只有第四种形式正确地将它们传递给您。那就是涉及Charset.forName("UTF-8").newDecoder()的那个。我在回答中对此做了一些解释。 是的,这样好多了。 更多出现输入编码错误而不是出现输出(至少如果它是 UTF 形式:8 位输出编码总是 lose-lose 在 Unicode 中。)但是,理论上你仍然可以在输出中产生它们,因为 Java 允许不成对的代理项存在于内存中的字符串中(它必须;这不是错误!),但不符合UTF-8,16,32 输出编码器允许在输出时生成它们。【参考方案4】:

从 Java 11 开始,您可以这样做:

FileWriter fw = new FileWriter("filename.txt", Charset.forName("utf-8"));

【讨论】:

【参考方案5】:

从 Java 7 开始,有一种简单的方法可以处理 BufferedWriter 和 BufferedReaders 的字符编码。您可以使用 Files 类直接创建 BufferedWriter,而不是创建 Writer 的各种实例。您可以通过调用简单地创建一个考虑字符编码的 BufferedWriter:

Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8);

您可以在 JavaDoc 中找到更多相关信息:

Files class Files#newBufferedWriter

【讨论】:

【参考方案6】:

对于中文文本,我尝试使用 Charset UTF-16,幸运的是它可以工作。

希望这能有所帮助!

PrintWriter out = new PrintWriter( file, "UTF-16" );

【讨论】:

可以试试 UTF-32【参考方案7】:

好的,现在是 2019 年,从 Java 11 开始,您有了一个带有 Charset 的构造函数:

FileWriter​(String fileName, Charset charset)

不幸的是,我们仍然无法修改字节缓冲区大小,而且它是 设置为 8192。(https://www.baeldung.com/java-filewriter)

【讨论】:

【参考方案8】:

使用OutputStream而不是FileWriter来设置编码类型

// file is your File object where you want to write you data 
OutputStream outputStream = new FileOutputStream(file);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
outputStreamWriter.write(json); // json is your data 
outputStreamWriter.flush();
outputStreamWriter.close();

【讨论】:

【参考方案9】:

在我看来

如果你想写follow kind UTF-8。你应该创建一个字节数组。然后,你可以这样做: byte[] by=("<?xml version=\"1.0\" encoding=\"utf-8\"?>"+"Your string".getBytes();

然后,您可以将每个字节写入您创建的文件中。 示例:

OutputStream f=new FileOutputStream(xmlfile);
    byte[] by=("<?xml version=\"1.0\" encoding=\"utf-8\"?>"+"Your string".getBytes();
    for (int i=0;i<by.length;i++)
    byte b=by[i];
    f.write(b);

    
    f.close();

【讨论】:

欢迎来到 Stack Overflow!虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性的 cmets 挤满你的代码,这会降低代码和解释的可读性!

以上是关于使用 FileWriter (Java) 以 UTF-8 格式写入文件?的主要内容,如果未能解决你的问题,请参考以下文章

J04-Java IO流总结二《 FileReader和FileWriter 》

在java中使用filewriter保存文件时如何设置目录?

JAVA基础复习- 字符流FileWriter

Java IO详解(四)------字符流

Java IO详解(四)------字符流

Java FileWriter类