Konstruktory bezpiecznego kodowania
Zmuszenie Javy do prawidłowego powiadamiania o błędach kodowania jest trudne. Musisz użyć najbardziej rozwlekłego i, niestety, najmniej używanego z czterech alternatywnych konstruktorów dla każdego z nich InputStreamReader
i OutputStreamWriter
otrzymać odpowiedni wyjątek od usterki kodowania.
W przypadku wejścia / wyjścia pliku zawsze upewnij się, że zawsze używasz drugiego argumentu obu OutputStreamWriter
i InputStreamReader
fantazyjnego argumentu kodera:
Charset.forName("UTF-8").newEncoder()
Istnieją inne, nawet bardziej wyszukane możliwości, ale żadna z trzech prostszych możliwości nie działa w przypadku obsługi wyjątków. Te robią:
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()
);
Jeśli chodzi o bieganie z
$ java -Dfile.encoding=utf8 SomeTrulyRemarkablyLongcLassNameGoeShere
Problem polega na tym, że nie użyje on pełnej formy argumentu kodera dla strumieni znaków, więc ponownie przegapisz problemy z kodowaniem.
Dłuższy przykład
Oto dłuższy przykład, ten zarządzający procesem zamiast plikiem, w którym promujemy dwa różne strumienie bajtów wejściowych i jeden strumień bajtów wyjściowych do strumieni znaków UTF-8 z pełną obsługą wyjątków :
Process
slave_process = Runtime.getRuntime().exec("perl -CS script args");
OutputStream
__bytes_into_his_stdin = slave_process.getOutputStream();
OutputStreamWriter
chars_into_his_stdin = new OutputStreamWriter(
__bytes_into_his_stdin,
Charset.forName("UTF-8").newEncoder()
);
InputStream
__bytes_from_his_stdout = slave_process.getInputStream();
InputStreamReader
chars_from_his_stdout = new InputStreamReader(
__bytes_from_his_stdout,
Charset.forName("UTF-8").newDecoder()
);
InputStream
__bytes_from_his_stderr = slave_process.getErrorStream();
InputStreamReader
chars_from_his_stderr = new InputStreamReader(
__bytes_from_his_stderr,
Charset.forName("UTF-8").newDecoder()
);
Teraz masz trzy strumienie znakowe że wszystko Wyjątek podbicie na błędy kodowania, odpowiednio nazwane chars_into_his_stdin
, chars_from_his_stdout
i chars_from_his_stderr
.
Jest to tylko trochę bardziej skomplikowane niż to, czego potrzebujesz do swojego problemu, którego rozwiązanie podałem w pierwszej połowie tej odpowiedzi. Najważniejsze jest to, że jest to jedyny sposób wykrywania błędów kodowania.
Tylko nie zaczynaj mi o PrintStream
wyjątkach żywieniowych.
InputStreamReader char_input = new InputStreamWriter
powinien przeczytać:,InputStreamReader char_input = new InputStreamReader
aInputStreamReader
konstruktor bierze aCharsetDecoder
, a nieCharsetEncoder
.CipherInputStream
, to usuwa jeBadPaddingException
, nawet jeśli są tworzone przez uwierzytelniony strumień szyfrowania :(Ditch
FileWriter
iFileReader
, które są bezużyteczne właśnie dlatego, że nie pozwalają na określenie kodowania. Zamiast tego użyjnew OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)
i
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
źródło
Charset.forName("UTF-8").newDecoder()
argumentu (lub jakiejś bardziej wyszukanej konstrukcji) zamiast po prostu"UTF-8"
, nie zostaniesz odpowiednio powiadomiony o błędach kodowania (czytaj: wyjątki zostaną zniesione i tajemniczo ukryje błędy kodowania).new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8 )
Musisz użyć
OutputStreamWriter
klasy jako parametru zapisującego dla swojegoBufferedWriter
. Akceptuje kodowanie. Przejrzyj javadocs .Trochę tak:
BufferedWriter out = new BufferedWriter(new OutputStreamWriter( new FileOutputStream("jedis.txt"), "UTF-8" ));
Lub możesz ustawić bieżące kodowanie systemu za pomocą właściwości systemowej
file.encoding
na UTF-8.java -Dfile.encoding=UTF-8 com.jediacademy.Runner arg1 arg2 ...
Możesz również ustawić go jako właściwość systemową w czasie wykonywania,
System.setProperty(...)
jeśli potrzebujesz go tylko dla tego konkretnego pliku, ale w takim przypadku myślę, że wolałbymOutputStreamWriter
.Ustawiając właściwość systemową, możesz używać
FileWriter
i oczekiwać, że będzie on używał UTF-8 jako domyślnego kodowania dla twoich plików. W tym przypadku dla wszystkich plików, które czytasz i zapisujesz.EDYTOWAĆ
Począwszy od API 19, możesz zamienić ciąg znaków „UTF-8” na
StandardCharsets.UTF_8
Jak zasugerował w komentarzach poniżej tchrist , jeśli zamierzasz wykryć błędy kodowania w swoim pliku, będziesz zmuszony użyć tego
OutputStreamWriter
podejścia i użyć konstruktora, który otrzymuje koder zestawu znaków.Coś jak
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));
Możesz wybierać między działaniami
IGNORE | REPLACE | REPORT
Również na to pytanie udzielono już tutaj odpowiedzi .
źródło
InputStreamReader(InputStream in, CharsetDecoder dec)
takiego, że ostatnim argumentem jestCharset.forName("UTF-8").newDecoder()
.{In,Out}putStream{Reader,Writer}
konstruktory na błędnych danych, odkryjesz, że trzy z nich maskują wszystkie wyjątki, które powinny wynikać z błędów kodowania, a tylko czwarta forma poprawnie je dostarcza. To jest ten, który obejmujeCharset.forName("UTF-8").newDecoder()
. Wyjaśniam to trochę w mojej odpowiedzi.Od wersji Java 11 możesz:
FileWriter fw = new FileWriter("filename.txt", Charset.forName("utf-8"));
źródło
Od Java 7 istnieje łatwy sposób obsługi kodowania znaków BufferedWriter i BufferedReaders. Możesz utworzyć BufferedWriter bezpośrednio przy użyciu klasy Files zamiast tworzenia różnych wystąpień Writer. Możesz po prostu utworzyć BufferedWriter, który uwzględnia kodowanie znaków, wywołując:
Więcej na ten temat można znaleźć w JavaDoc:
źródło
W przypadku tekstu chińskiego próbowałem użyć zestawu znaków UTF-16 i na szczęście działa.
Mam nadzieję, że to może pomóc!
PrintWriter out = new PrintWriter( file, "UTF-16" );
źródło
OK, teraz jest 2019, a od Java 11 masz konstruktora z Charset:
źródło
użyj OutputStream zamiast FileWriter, aby ustawić typ kodowania
// 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();
źródło
W mojej opinii
Jeśli chcesz pisać według rodzaju UTF-8, powinieneś utworzyć tablicę bajtów, a następnie wykonać następujące czynności:
byte[] by=("<?xml version=\"1.0\" encoding=\"utf-8\"?>"+"Your string".getBytes();
Następnie możesz zapisać każdy bajt do utworzonego pliku. Przykład:
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();
źródło