Jak przekonwertować Reader na InputStream i Writer na OutputStream?

87

Czy istnieje łatwy sposób na uniknięcie problemów z kodowaniem tekstu?

Andrei Savu
źródło

Odpowiedzi:

45

Naprawdę nie możesz uniknąć problemów z kodowaniem tekstu, ale istnieją rozwiązania w Apache Commons:

Musisz tylko wybrać wybrane przez siebie kodowanie.

Piotr
źródło
7
FYI: kod ReaderInputStream ma błąd w sposobie odczytu bajtów (nie będzie działać dla wszystkich kodowań). Dowód: nielegalnyargumentexception.blogspot.com/2009/05/ ... Wystąpił otwarty błąd: Issues.apache.org/bugzilla/show_bug.cgi?id=40455
McDowell
1
Zajęcia można znaleźć w bibliotece commons-io Apache: commons.apache.org/proper/commons-io
AlikElzin-kilaka
@McDowell, błąd, o którym wspomniałeś, występuje w implementacji Apache Ant, a nie w oprogramowaniu commons-io, więc nie ma znaczenia dla tej odpowiedzi.
Roman
94

Jeśli zaczynasz od String, możesz również wykonać następujące czynności:

new ByteArrayInputStream(inputString.getBytes("UTF-8"))
Ritesh Tendulkar
źródło
7
Dobra ReaderInputStreamimplementacja wymagałaby mniej pamięci - nie powinno być potrzeby jednoczesnego przechowywania wszystkich bajtów w tablicy.
Piotr Findeisen
3
Podoba mi się to rozwiązanie, ponieważ działa, gdy trzeba testować kod, który akceptuje dane wejściowe na (np.) Standardowym wejściu.
Kedar Mhaswade
43

Cóż, Reader zajmuje się znakami, a InputStream zajmuje się bajtami. Kodowanie określa, w jaki sposób chcesz przedstawić swoje znaki jako bajty, więc nie możesz tak naprawdę zignorować problemu. Jeśli chodzi o unikanie problemów, moim zdaniem: wybierz jeden zestaw znaków (np. „UTF-8”) i trzymaj się go.

Jeśli chodzi o to, jak to zrobić, jak wskazano, „ oczywistymi nazwami tych klas są ReaderInputStream i WriterOutputStream . ” Co zaskakujące, „ nie są one zawarte w bibliotece Java ”, mimo że „przeciwne” klasy, InputStreamReader i OutputStreamWriter w zestawie.

Tak więc wiele osób wymyśliło własne implementacje, w tym Apache Commons IO . W zależności od kwestii licencyjnych prawdopodobnie będziesz w stanie włączyć bibliotekę commons-io do swojego projektu, a nawet skopiować część kodu źródłowego (który można pobrać tutaj ).

Jak widać, dokumentacja obu klas stwierdza, że ​​„wszystkie kodowania zestawów znaków obsługiwane przez środowisko JRE są obsługiwane poprawnie”.

Uwaga: komentarz do jednej z pozostałych odpowiedzi wspomina o tym błędzie . Ma to jednak wpływ na klasę Apache Ant ReaderInputStream ( tutaj ), a nie na klasę Apache Commons IO ReaderInputStream.

Peter Ford
źródło
19

Zauważ również, że jeśli zaczynasz od String, możesz pominąć tworzenie StringReader i utworzyć InputStream w jednym kroku, używając org.apache.commons.io.IOUtils z Commons IO, tak jak to:

InputStream myInputStream = IOUtils.toInputStream(reportContents, "UTF-8");

Oczywiście nadal musisz pomyśleć o kodowaniu tekstu, ale przynajmniej konwersja odbywa się w jednym kroku.

Phil Harvey
źródło
4
Ta metoda zasadniczo new ByteArrayInputStream(report.toString().getBytes("utf-8"))polega na przydzieleniu dwóch dodatkowych kopii raportu w pamięci. Jeśli raport jest obszerny, jest zły. Zobacz moją odpowiedź.
Oliv
8

Posługiwać się:

new CharSequenceInputStream(html, StandardCharsets.UTF_8);

W ten sposób nie wymaga wcześniejszej konwersji na, Stringa następnie nabyte[] , co przydziela dużo więcej pamięci sterty na wypadek, gdyby raport był duży. Konwertuje na bajty w locie, gdy strumień jest odczytywany, bezpośrednio z StringBuffer.

Używa CharSequenceInputStream z projektu Apache Commons IO.

Oliv
źródło
5

Oczywiste nazwy tych klas to ReaderInputStream i WriterOutputStream. Niestety nie są one zawarte w bibliotece Java. Jednak Google to twój przyjaciel.

Nie jestem pewien, czy obejdzie wszystkie problemy z kodowaniem tekstu, które są koszmarne.

Istnieje RFE, ale jest zamknięty, nie naprawi.

Tom Hawtin - haczyk
źródło
1
bugs.openjdk.java.net/browse/JDK-4103785 zawiera komentarz „mamy publiczny interfejs API do kodowania zestawu znaków ... nie ma ważnego powodu, aby dodawać te klasy” - więc jak to zrobić w Javie 7, bez dodatkowych biblioteki, dwanaście lat później?
Piotr Findeisen
5

Nie możesz uniknąć problemów z kodowaniem tekstu, ale Apache commons-io tak

Zwróć uwagę, że są to biblioteki, do których odnosi się odpowiedź Piotra na koders.com, a jedynie łącza do biblioteki zamiast kodu źródłowego.

dfrankow
źródło
4

Czy próbujesz zapisać zawartość od a Readerdo an OutputStream? Jeśli tak, łatwiej będzie ci zawinąć OutputStreamw an OutputStreamWriteri zapisać chars od the Readerdo Writer, zamiast próbować przekonwertować czytnik na InputStream:

final Writer writer = new BufferedWriter(new OutputStreamWriter( urlConnection.getOutputStream(), "UTF-8" ) );
int charsRead;
char[] cbuf = new char[1024];
while ((charsRead = data.read(cbuf)) != -1) {
    writer.write(cbuf, 0, charsRead);
}
writer.flush();
// don't forget to close the writer in a finally {} block
Sam Barnum
źródło
1

Ostrzeżenie podczas korzystania z WriterOutputStream - nie zawsze obsługuje poprawnie zapisywanie danych binarnych do pliku / tak samo jak zwykły strumień wyjściowy. Miałem z tym problem, którego wytropienie zajęło mi trochę czasu.

Jeśli możesz, polecam użycie strumienia wyjściowego jako podstawy, a jeśli chcesz napisać ciągi, użyj otoki OUtputStreamWriter wokół strumienia, aby to zrobić. O wiele bardziej niezawodne jest przekonwertowanie tekstu na bajty niż na odwrót, co prawdopodobnie powoduje, że WriterOutputStream nie jest częścią standardowej biblioteki Java

romeara
źródło
-1

Do czytania łańcucha w strumieniu przy użyciu tego, co dostarcza Java.

InputStream s = new BufferedInputStream( new ReaderInputStream( new StringReader("a string")));
Aaron
źródło
6
ReaderInputStream znajduje się w Apache Commons IO.
Will Beason