Написати файл в 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();
}

Відповіді:


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конструктор бере a CharsetDecoder, а не a CharsetEncoder.
Марк Роудс

Але чи є це справжньою проблемою, що UTF-8 не представляє, я думав, що це може кодувати що завгодно.
Пол Тейлор,

Якщо ви хочете поскаржитися на винятки, що їдять потоки, спробуйте CipherInputStreamвидалити BadPaddingExceptionїх, навіть якщо вони створені автентифікованим потоком шифру :(
Maarten Bodewes,

Я виявив невелику помилку у вашому коді: "Charset.forName (" UTF-8 "). NewEncoder ()" для "InputStreamReader" має бути "Charset.forName (" UTF-8 "). NewDecoder ()". Тож "декодер" замість "кодер". Але в будь-якому випадку, дякую за цю приємну відповідь та +1. :)
codepleb

2
(У всій системі
вводу-

56

Ditch 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", ви не будете належним чином повідомлені про помилки кодування (читайте: винятки будуть придушені, і це таємниче приховає помилки кодування).
christ

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"
));

Або ви можете встановити поточне кодування системи із властивістю системи 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().
christ

1
Якщо ви це зробите, помилки кодування вводу будуть мовчки скинуті.
trist

Кодер не потрібен. Конструктор приймає String, Charset або Encoder в обох класах Input / Output. Не впевнені, що ви маєте на увазі під своїм коментарем. Чи можете ви розказати, будь ласка?
Едвін Далорцо,

3
@edalorzo Якщо ви випробуєте чотири різні {In,Out}putStream{Reader,Writer}конструктори на помилкових даних, ви виявите, що три з них маскують усі винятки, які мають виникнути внаслідок помилок кодування, і лише четверта форма правильно їх вам доставляє. Це той, який передбачає Charset.forName("UTF-8").newDecoder(). Я трохи пояснюю це у своїй відповіді.
christ

1
Так, це набагато краще. Це багато трапляється частіше з помилками кодування вводу, коли це виникає, ніж з результатом (принаймні, якщо це форма UTF: 8-бітові кодування виводу завжди втрачають і втрачають в Unicode.) Однак теоретично ви все одно можете їх спричинити на виході , тому що Java дозволяє непарних сурогати існувати в рядках в пам'яті (вона має до ,! це не помилка), але не сумісну UTF- {8,16,32} вихід енкодера забороняється виробляти їх на виході.
tchrist

9

З Java 11 ви можете робити:

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

7

З Java 7 існує простий спосіб обробити кодування символів BufferedWriter та BufferedReaders. Ви можете створити BufferedWriter безпосередньо, використовуючи клас Files, замість того, щоб створювати різні екземпляри Writer. Ви можете просто створити 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! Незважаючи на те, що цей фрагмент коду може вирішити питання, включення пояснення дійсно допомагає поліпшити якість вашої публікації. Пам’ятайте, що ви будете відповідати на запитання для читачів у майбутньому, і ці люди можуть не знати причин вашої пропозиції коду. Будь ласка, також намагайтеся не переповнювати свій код пояснювальними коментарями, це зменшує читабельність як коду, так і пояснень!
Ісія Медоус
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.