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


82

我有以下代码,但是,我希望它以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();
}

Answers:


77

安全编码构造函数

使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

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

只是不要让我开始PrintStream进食异常。


1
很好的答案,但是我认为它有一个小错误-InputStreamReader char_input = new InputStreamWriter应该读为:InputStreamReader char_input = new InputStreamReader InputStreamReader构造函数使用a CharsetDecoder,而不是a CharsetEncoder
马克·罗德斯

但这是一个真正的问题吗,UTF-8不可能代表什么,我认为它可以编码任何东西。
保罗·泰勒

如果您想抱怨流吃异常,请尝试CipherInputStream删除BadPaddingException,即使它们是由经过身份验证的密码流创建的:(
Maarten Bodewes

我在您的代码中发现了一个小错误:“ InputStreamReader”的“ Charset.forName(“ UTF-8”)。newEncoder()”应为“ Charset.forName(“ UTF-8”)。newDecoder()”。所以用“解码器”代替“编码器”。但是,无论如何,感谢您的出色回答和+1。:)
codepleb 2014年

2
(整个Java IO系统一直都是一片混乱。应该像Joda Time的日期一样进行完全的重新设计。)
Tuntable '19

56

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

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

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


12
如果您不使用非常冗长的Charset.forName("UTF-8").newDecoder()参数(或某些怪异的构造)而不是just "UTF-8",则不会正确地通知您编码错误(阅读:异常将被抑制,并且会神秘地隐藏编码错误)。
tchrist 2012年

3
new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8 )
阿卜杜勒

46

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

有点像这样:

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

或者,您可以使用system属性将当前系统编码设置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在下面的注释中所建议的那样,如果您打算检测文件中的编码错误,则将不得不使用该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

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


这还不够。您还需要一个InputStreamReader(InputStream in, CharsetDecoder dec),使得最后一个参数是Charset.forName("UTF-8").newDecoder()
tchrist 2012年

1
如果这样做,将自动删除输入编码错误。
tchrist 2012年

不需要编码器。构造函数在两个Input / Output类中都接受String,Charset或Encoder。不知道您的评论是什么意思。请详细说明一下吗?
Edwin Dalorzo 2012年

3
@edalorzo如果您{In,Out}putStream{Reader,Writer}对错误的数据测试了四个不同的构造器,您会发现其中三个掩盖应该由编码错误引起的所有异常,只有第四个形式才能正确地将它们传递给您。那就是涉及的那个Charset.forName("UTF-8").newDecoder()。我在回答中对此做了一些解释。
tchrist 2012年

1
是的,那要好得多。这是 很多更经常与输入编码错误在那里这来了比它输出来了(至少如果它是一个UTF形式:8位输出编码始终是两败俱伤。以Unicode)但是,你可以在理论上仍然招致他们因为Java允许内存中的字符串中存在成对的代理(它必须这样做;这不是bug!),但是不允许一致的UTF- {8,16,32}输出编码器在输出中生成它们。
tchrist 2012年

9

从Java 11开始,您可以执行以下操作:

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

7

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

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

您可以在JavaDoc中找到有关它的更多信息:


5

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

希望这会有所帮助!

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

可以尝试使用UTF-32
anson

1

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

FileWriter​(String fileName, Charset charset)

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


0

使用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();

-3

在我看来

如果您想按照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!尽管此代码段可以解决问题,但提供说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。也请尽量不要在代码中加入解释性注释,这会降低代码和解释的可读性!
伊西亚·梅多斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.