Jeśli masz java.io.InputStream
obiekt, jak go przetworzyć i wygenerować String
?
Załóżmy, że mam plik InputStream
tekstowy i chcę go przekonwertować na plik String
, aby na przykład zapisać go w pliku dziennika.
Jaki jest najłatwiejszy sposób, aby wziąć InputStream
i przekonwertować go na String
?
public String convertStreamToString(InputStream is) {
// ???
}
ByteArrayOutputStream outputBytes = new ByteArrayOutputStream();
for(byte[] b = new byte[512]; 0 < inputStream.read(b); outputBytes.write(b));
return new String(outputBytes.toByteArray(), StandardCharsets.UTF_8);
String s = Files.readString(Path.of("SomeFile.txt"));
który jest tak dobry, jak tylko język, który nigdy nie będzie obsługiwał konwersji typu magicznego, takich jak opisany.Odpowiedzi:
Dobrym sposobem na to jest użycie wspólnego Apache
IOUtils
do skopiowaniaInputStream
doStringWriter
... czegoś podobnegolub nawet
Alternatywnie możesz użyć,
ByteArrayOutputStream
jeśli nie chcesz mieszać swoich strumieni i pisarzyźródło
Podsumuj inne odpowiedzi Znalazłem 11 głównych sposobów, aby to zrobić (patrz poniżej). I napisałem kilka testów wydajności (patrz wyniki poniżej):
Sposoby konwersji InputStream na ciąg znaków:
Korzystanie
IOUtils.toString
(Apache Utils)Korzystanie
CharStreams
(Guava)Korzystanie
Scanner
(JDK)Korzystanie ze Stream API (Java 8). Ostrzeżenie : To rozwiązanie przekształca różne podziały linii (jak
\r\n
) na\n
.Korzystanie z interfejsu API Stream równoległego (Java 8). Ostrzeżenie : To rozwiązanie przekształca różne podziały linii (jak
\r\n
) na\n
.Korzystanie
InputStreamReader
iStringBuilder
(JDK)Korzystanie
StringWriter
iIOUtils.copy
(Apache Commons)Korzystanie
ByteArrayOutputStream
iinputStream.read
(JDK)Korzystanie
BufferedReader
(JDK). Ostrzeżenie: to rozwiązanie przekształca różne podziały wiersza (jak\n\r
) weline.separator
właściwość systemową (na przykład w systemie Windows na „\ r \ n”).Korzystanie
BufferedInputStream
iByteArrayOutputStream
(JDK)Używanie
inputStream.read()
iStringBuilder
(JDK). Ostrzeżenie : to rozwiązanie ma problemy z Unicode, na przykład z tekstem rosyjskim (działa poprawnie tylko z tekstem innym niż Unicode)Ostrzeżenie :
Rozwiązania 4, 5 i 9 przekształcają różne podziały linii na jeden.
Rozwiązanie 11 nie działa poprawnie z tekstem Unicode
Testy wydajności
Testy wydajności dla małych
String
(długość = 175), URL w github (tryb = średni czas, system = Linux, wynik 1,343 jest najlepszy):Testy wydajności dla big
String
(długość = 50100), url w github (tryb = średni czas, system = Linux, wynik 200,715 jest najlepszy):Wykresy (testy wydajności w zależności od długości strumienia wejściowego w systemie Windows 7)
Test wydajności (średni czas) w zależności od długości strumienia wejściowego w systemie Windows 7:
źródło
\r\n
), Na\n
które w niektórych przypadkach mogą być niepożądane. Byłoby również miło zobaczyć wymaganą dodatkową pamięć lub przynajmniej presję alokacji (przynajmniej możesz uruchomić JMH-prof gc
). W przypadku naprawdę fajnego postu byłoby dobrze zobaczyć wykresy (w zależności od długości łańcucha w tym samym rozmiarze wejściowym i zależnie od rozmiaru wejściowego w tej samej długości łańcucha).reset()
na przykład w 11?Oto sposób korzystania tylko ze standardowej biblioteki Java (pamiętaj, że strumień nie jest zamknięty, twój przebieg może się różnić).
Nauczyłem się tej sztuczki z artykułu „Triki z głupiego skanera” . Powodem tego jest to, że skaner dokonuje iteracji po tokenach w strumieniu. W tym przypadku tokenów oddzielamy za pomocą „początku granicy wejściowej” (\ A), co daje nam tylko jeden token dla całej zawartości strumienia.
Uwaga: jeśli musisz sprecyzować kodowanie strumienia wejściowego, możesz podać drugi
Scanner
konstruktor, który wskazuje, jakiego zestawu znaków użyć (np. „UTF-8”).Wskazówka kapelusza trafia także do Jakuba , który kiedyś wskazał mi ten artykuł.
źródło
if (is == null) return "";
na samym początku metody; Uważam, że ta odpowiedź musi zostać zaktualizowana, aby lepiej obsługiwać strumienie wejściowe o wartości zerowej.try(java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; }
Apache Commons pozwala:
Oczywiście możesz wybrać inne kodowanie znaków oprócz UTF-8.
Zobacz także: ( dokumentacja )
źródło
Biorąc pod uwagę plik należy najpierw uzyskać
java.io.Reader
instancję. Można to następnie odczytać i dodać doStringBuilder
(nie potrzebujemy,StringBuffer
jeśli nie uzyskujemy do niego dostępu w wielu wątkach iStringBuilder
jest to szybsze). Sztuczka polega na tym, że pracujemy w blokach i jako takie nie potrzebujemy innych strumieni buforujących. Rozmiar bloku jest parametryzowany w celu optymalizacji wydajności w czasie wykonywania.źródło
In our product, I even replaced
powinno być „nawet zastąpiliśmy”.Posługiwać się:
źródło
readLine
odczytuje znak po znaku, aby wyszukać EOL. Ponadto, jeśli w strumieniu nie ma podziału linii, nie ma to naprawdę sensu.Jeśli korzystasz z Google-Collections / Guava, możesz wykonać następujące czynności:
Zauważ, że drugi parametr (tj. Charsets.UTF_8) dla parametru
InputStreamReader
nie jest konieczny, ale ogólnie dobrze jest podać kodowanie, jeśli go znasz (co powinieneś!)źródło
To najlepsze czyste oprogramowanie Java, które idealnie pasuje do Androida i każdej innej maszyny JVM.
To rozwiązanie działa niesamowicie dobrze ... jest proste, szybkie i działa tak samo na małych i dużych strumieniach !! (patrz punkt odniesienia powyżej .. nr 8 )
źródło
2*n
, gdzie n jest rozmiarem strumienia, zgodnie z systememByteArrayInputStream
automatycznego wzrostu.Dla kompletności oto rozwiązanie Java 9 :
readAllBytes
Jest obecnie w JDK 9 głównym kodzie, więc jest prawdopodobne, aby pojawić się w komunikacie. Możesz wypróbować teraz, korzystając z kompilacji migawek JDK 9 .źródło
byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
gdzieMAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
co dajeMAX_BUFFER_SIZE = 2147483639
. Google twierdzi, że ma około 2.147 GB.InputStream
, a nie oPath
.InputStream
Mogą być tworzone z wielu różnych źródeł, a nie tylko pliki.byte[]
wdrożenie jeśli wszystkie znaki są w pierwszych 256 punktów kodowych. Oznacza to, że nowy ciąg (bajt [], „ISO-Latin-1”) będzie prostą kopią tablicy.Posługiwać się:
źródło
BufferedInputStream
. Podstawowe odczyty to 8192 bajtów naraz.BufferedInputStream
i czytanie w buforze tablicy bajtów zamiast jednego bajtu na raz. Przykład: 200 ms w porównaniu do 60 ms podczas czytania pliku 4.56 MiB.buf.toString()
.Oto najbardziej eleganckie rozwiązanie w czystej Javie (bez biblioteki), które wymyśliłem po kilku eksperymentach:
źródło
InputStream
powinien zostać zamknięty przez dzwoniącego.readLine
? jeśli nie używasz linii per se, co to zaZrobiłem punkt odniesienia na podstawie 14 różnych odpowiedzi tutaj (przepraszam, że nie dostałem kredytów, ale jest zbyt wiele duplikatów).
Wynik jest bardzo zaskakujący. Okazuje się, że Apache IOUtils jest najwolniejszym i
ByteArrayOutputStream
najszybszym rozwiązaniem:Więc najpierw jest najlepsza metoda:
Wyniki testu, z 20 MB losowych bajtów w 20 cyklach
Czas w milisekundach
Kod źródłowy testu porównawczego
źródło
Użyłbym kilku sztuczek Java 8.
Zasadniczo takie same jak niektóre inne odpowiedzi, z wyjątkiem bardziej zwięzłych.
źródło
return null
kiedykolwiek to się nazywa? Zgłaszane sąbr.lines...
zwroty lub wyjątek.parallel()
w strumieniu?\r\n
zostałyby przekonwertowane na\n
...System.lineSeparator()
aby użyć odpowiedniego zakończenia linii zależnego od platformy.Przeprowadziłem testy czasowe, ponieważ czas zawsze ma znaczenie.
Próbowałem uzyskać odpowiedź na ciąg 3 różnymi sposobami. (pokazano poniżej)
Pominąłem bloki try / catch ze względu na czytelność.
Aby podać kontekst, jest to poprzedni kod dla wszystkich 3 podejść:
1)
2)
3)
Po przeprowadzeniu 500 testów dla każdego podejścia z tymi samymi danymi żądania / odpowiedzi, oto liczby. Ponownie, są to moje ustalenia i twoje odkrycia mogą nie być dokładnie takie same, ale napisałem to, aby dać innym wskazówkę na temat różnic w wydajności tych podejść.
Rangi:
Podejście nr 1
Podejście nr 3 - 2,6% wolniej niż nr 1
Podejście nr 2 - 4,3% wolniej niż nr 1
Każde z tych podejść jest odpowiednim rozwiązaniem do pobrania odpowiedzi i utworzenia z niej ciągu.
źródło
Rozwiązanie Pure Java wykorzystujące strumień Stream działa od wersji Java 8.
Jak wspomniał Christoffer Hammarström poniżej innej odpowiedzi , bezpieczniej jest wyraźnie określić zestaw znaków . Tzn. Konstruktor InputStreamReader można zmieniać w następujący sposób:
źródło
Charset.forName("UTF-8")
użyjStandardCharsets.UTF_8
(odjava.nio.charset
).Oto mniej więcej odpowiedź sampath, nieco oczyszczona i przedstawiona jako funkcja:
źródło
Jeśli masz ochotę na przygodę, możesz połączyć Scalę i Javę i skończyć z tym:
Mieszanie kodu Java i Scali z bibliotekami ma swoje zalety.
Zobacz pełny opis tutaj: Idiomatyczny sposób konwersji InputStream na ciąg znaków w Scali
źródło
Source.fromInputStream(...).mkString
Jeśli nie możesz użyć Commons IO (FileUtils / IOUtils / CopyUtils), oto przykład użycia BufferedReadera do odczytu pliku linia po linii:
Lub jeśli chcesz surowej prędkości, zaproponowałbym odmianę tego, co zasugerował Paul de Vrieze (która unika korzystania z StringWriter (który korzysta z StringBuffer wewnętrznie):
źródło
Ten jest miły, ponieważ:
Jak to zrobić?
Dla JDK 9
źródło
catch (Throwable)
Naprawdę nie powinno być puste, jeśli jest to kod produkcji.Jest to odpowiedź zaadaptowana z
org.apache.commons.io.IOUtils
kodu źródłowego dla tych, którzy chcą mieć implementację apache, ale nie chcą całej biblioteki.źródło
Pamiętaj, aby zamknąć strumienie na końcu, jeśli używasz czytników strumieniowych
EDYCJA: W JDK 7+ można użyć konstrukcji try-with-resources.
źródło
iStream
powinien raczej zostać zamknięty przez dzwoniącego, ponieważ dzwoniący został utworzonyiStream
. Poza tym zamykanie strumieni powinno odbywać się wfinally
bloku, a jeszcze lepiej w instrukcji try-with-resources Java 7. W twoim kodzie, gdyreadLine()
rzucaIOException
lubbuilder.append()
rzucaOutOfMemoryError
, strumienie pozostaną otwarte.Kolejny, dla wszystkich użytkowników Spring:
Metody narzędzi w
org.springframework.util.StreamUtils
są podobne do tych wFileCopyUtils
, ale po zakończeniu pozostawiają strumień otwarty.źródło
Użyj java.io.InputStream.transferTo (OutputStream) obsługiwanego w Javie 9 i ByteArrayOutputStream.toString (String), który przyjmuje nazwę zestawu znaków:
źródło
Oto pełna metoda konwersji
InputStream
naString
bez korzystania z biblioteki innej firmy. UżyjStringBuilder
do środowiska jednowątkowego, w przeciwnym razie użyjStringBuffer
.źródło
in = new InputStreamReader(inputStream)
i(char)in.read()
.Oto jak to zrobić, używając tylko JDK przy użyciu buforów tablicy bajtów. Tak właśnie
IOUtils.copy()
działają wszystkie metody commons-io . Można wymienićbyte[]
zechar[]
jeśli kopiowania zReader
zamiastInputStream
.źródło
Użytkownicy Kotlin po prostu:
natomiast
jest wbudowaną metodą rozszerzenia biblioteki standardowej Kotlin.
źródło
is.bufferedReader().use { it.readText() }
.Najprostszym sposobem w JDK jest użycie następujących fragmentów kodu.
źródło
Oto moje rozwiązanie oparte na Javie 8 , które korzysta z nowego API Stream do zbierania wszystkich linii z
InputStream
:źródło
Pod względem
reduce
iconcat
można to wyrazić w Javie 8 jako:źródło
StringBuilder
może być bardziej wydajny. Sprawdzę, ale moim celem było pokazanie bardziej funkcjonalnego podejścia z niezmiennymString
.Odpowiedź JDK 7/8, która zamyka strumień i nadal zgłasza wyjątek IOException:
źródło