Написать файл в UTF-8 с помощью FileWriter (Java)?

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();
}
user1280970
источник

Ответы:

77

Конструкторы безопасного кодирования

Заставить Java правильно уведомлять вас об ошибках кодирования непросто. Вы должны использовать наиболее подробный и, увы, наименее используемый из четырех альтернативных конструкторов для каждого из InputStreamReaderи OutputStreamWriterдля получения надлежащего исключения при сбое кодирования.

Для файлового ввода-вывода всегда обязательно используйте в качестве второго аргумента OutputStreamWriterи InputStreamReaderаргумент причудливого кодировщика:

  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_stdin, chars_from_his_stdoutи chars_from_his_stderr.

Это лишь немного сложнее, чем то, что вам нужно для решения вашей проблемы, решение которой я дал в первой половине этого ответа. Ключевым моментом является то, что это единственный способ обнаружить ошибки кодирования.

Только не заставляйте меня начинать насчет PrintStreamисключений для еды.

Христос
источник
1
Отличный ответ, но я думаю, что в нем есть небольшая ошибка - InputStreamReader char_input = new InputStreamWriterследует читать:, InputStreamReader char_input = new InputStreamReader а InputStreamReaderконструктор принимает, а CharsetDecoderне CharsetEncoder.
Марк Роудс,
Но действительно ли это проблема, то, что UTF-8 не может представить, я думал, что он может кодировать что угодно.
Пол Тейлор
Если вы хотите пожаловаться на потоки, потребляющие исключения, попробуйте CipherInputStream, это удалит BadPaddingException's, даже если они созданы аутентифицированным зашифрованным потоком :(
Maarten Bodewes
Я обнаружил небольшую ошибку в вашем коде: «Charset.forName (« UTF-8 »). NewEncoder ()» для «InputStreamReader» должно быть «Charset.forName (« UTF-8 »). NewDecoder ()». Так что «декодер» вместо «кодировщик». Но в любом случае спасибо за этот хороший ответ и +1. :)
codepleb 08
2
(Вся система ввода-вывода Java всегда была в беспорядке. Следует полностью переработать, как переработанные даты Joda Time.)
Tuntable
56

Угробите FileWriterи FileReader, которые бесполезны именно потому, что не позволяют указать кодировку. Вместо этого используйте

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

и

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

Майкл Боргвардт
источник
12
Если вы не используете очень подробный Charset.forName("UTF-8").newDecoder()аргумент (или какую-то более причудливую конструкцию) вместо просто "UTF-8", вы не будете должным образом уведомлены об ошибках кодирования (читайте: исключения будут подавлены, и это таинственным образом скроет ошибки кодирования).
tchrist
3
new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8 )
Абдул
46

Вам необходимо использовать OutputStreamWriterкласс в качестве параметра записи для вашего 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().
Христос
1
Ошибки входной кодировки будут автоматически сброшены, если вы это сделаете.
Христос
В кодировщике нет необходимости. Конструктор принимает либо String, Charset, либо Encoder в обоих классах ввода / вывода. Не уверен, что вы имеете в виду под своим комментарием. Не могли бы вы уточнить, пожалуйста?
Эдвин Далорцо,
3
@edalorzo Если вы протестируете четыре разных {In,Out}putStream{Reader,Writer}конструктора на ошибочных данных, вы обнаружите, что три из них маскируют все исключения, которые должны возникать из-за ошибок кодирования, и только четвертая форма правильно передает их вам. Это тот, который включает Charset.forName("UTF-8").newDecoder(). Я немного объясню это в своем ответе.
Христос
1
Да, это намного лучше. Это происходит гораздо чаще с ошибками входной кодировки, чем с выходными (по крайней мере, если это форма UTF: 8-битные выходные кодировки всегда проигрывают в Юникоде). Однако теоретически вы все равно можете их понести. на выходе , потому что Java позволяет непарных суррогаты существовать в строках в памяти (она имеет к,! это не ошибка), но не совместимую UTF- {8,16,32} выход энкодера не разрешается производить их на выходе.
Христос
9

Начиная с Java 11 вы можете:

FileWriter fw = new FileWriter("filename.txt", Charset.forName("utf-8"));
Mortensi
источник
7

Начиная с Java 7, существует простой способ обработки символьной кодировки BufferedWriter и BufferedReaders. Вы можете создать BufferedWriter напрямую, используя класс Files, вместо создания различных экземпляров Writer. Вы можете просто создать BufferedWriter, который учитывает кодировку символов, вызвав:

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

Вы можете найти больше об этом в JavaDoc:

Ларс Брим
источник
5

С китайским текстом я попытался использовать кодировку UTF-16, и, к счастью, она сработала.

Надеюсь, это поможет!

PrintWriter out = new PrintWriter( file, "UTF-16" );
Phuong
источник
можно попробовать с UTF-32
ответчик
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! Хотя этот фрагмент кода может решить вопрос, включение объяснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин вашего предложения кода. Также постарайтесь не загромождать свой код пояснительными комментариями, это снижает удобочитаемость как кода, так и пояснений!
Isiah Meadows