【问题标题】:Write a file in UTF-8 using FileWriter (Java)?使用 FileWriter (Java) 以 UTF-8 格式写入文件?
【发布时间】: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();
}

【问题讨论】:

    标签: java file-io unicode utf-8 file-format


    【解决方案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 STD{IN,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,Out}putStream{Reader,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 中找到更多相关信息:

      【讨论】:

        【解决方案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 挤满你的代码,这会降低代码和解释的可读性!
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-03-23
            • 1970-01-01
            • 2011-03-01
            • 1970-01-01
            • 2016-01-30
            • 1970-01-01
            • 2020-08-09
            相关资源
            最近更新 更多