Guava-equivalent voor IOUtils.toString(InputStream)

Apache Commons IOheeft een handige methode IOUtils.toString()om een ​​InputStreamnaar een tekenreeks.

Aangezien ik probeer over te stappen van Apache Commons en naar Guava: is is er een equivalent in Guava? Ik heb alle klassen in het com.google.common.io-pakket bekeken en ik kon niets zo eenvoudigs vinden.

Bewerken:ik begrijp en waardeer de problemen met tekensets. Het is gewoon zo dat ik weet dat al mijn bronnen in ASCII zijn (ja, ASCII, niet ANSI enz.), dus in dit geval is codering geen probleem voor mij.


Antwoord 1, autoriteit 100%

U gaf in uw commentaar op het antwoord van Calum aan dat u ging gebruiken

CharStreams.toString(new InputStreamReader(supplier.get(), Charsets.UTF_8))

Deze code is problematisch omdat de overbelasting CharStreams.toString(Readable)stelt:

Sluit de Readableniet af.

Dit betekent dat uw InputStreamReader, en bij uitbreiding de InputStreamdie wordt geretourneerd door supplier.get(), na deze code niet wordt gesloten voltooid.

Als u daarentegen profiteert van het feit dat u al een InputSupplier<InputStream>lijkt te hebben en de overbelasting CharStreams.toString(InputSupplier<R extends Readable & Closeable>), zal de toString-methode zowel het maken als het sluiten van de Readervoor u afhandelen.

Dit is precies wat Jon Skeet suggereerde, behalve dat er eigenlijk geen overbelasting is van CharStreams.newReaderSupplierdie een InputStreamals invoer neemt… geef het een InputSupplier:

InputSupplier<? extends InputStream> supplier = ...
InputSupplier<InputStreamReader> readerSupplier = 
    CharStreams.newReaderSupplier(supplier, Charsets.UTF_8);
// InputStream and Reader are both created and closed in this single call
String text = CharStreams.toString(readerSupplier);

Het doel van InputSupplieris om uw leven gemakkelijker te maken door Guava de onderdelen te laten afhandelen die een lelijk try-finally-blok vereisen om ervoor te zorgen dat bronnen correct worden afgesloten.

Bewerken:Persoonlijk vind ik het volgende (zoals ik het eigenlijk zou schrijven, was gewoon de stappen in de bovenstaande code opsplitsen)

String text = CharStreams.toString(
    CharStreams.newReaderSupplier(supplier, Charsets.UTF_8));

om veelminder uitgebreid te zijn dan dit:

String text;
InputStreamReader reader = new InputStreamReader(supplier.get(), 
    Charsets.UTF_8);
boolean threw = true;
try {
  text = CharStreams.toString(reader);
  threw = false;
}
finally {
  Closeables.close(reader, threw);
}

Dat is min of meer wat je zou moeten schrijven om dit zelf goed af te handelen.


Bewerken: februari 2014

InputSupplieren OutputSupplieren de methoden die ze gebruiken, zijn verouderd in Guava 16.0. Hun vervangers zijn ByteSource, CharSource, ByteSinken CharSink. Gegeven een ByteSource, kun je de inhoud ervan nu als een Stringals volgt krijgen:

ByteSource source = ...
String text = source.asCharSource(Charsets.UTF_8).read();

Antwoord 2, autoriteit 65%

Als je een Readablehebt, kun je CharStreams.toString(Readable)gebruiken. U kunt dus waarschijnlijk het volgende doen:

String string = CharStreams.toString( new InputStreamReader( inputStream, "UTF-8" ) );

Dwingt je om een ​​tekenset op te geven, wat ik denk dat je sowieso zou moeten doen.


Antwoord 3, autoriteit 20%

Bijna. Je zou zoiets als dit kunnen gebruiken:

InputSupplier<InputStreamReader> readerSupplier = CharStreams.newReaderSupplier
    (streamSupplier, Charsets.UTF_8);
String text = CharStreams.toString(readerSupplier);

Persoonlijk denk ik nietdat IOUtils.toString(InputStream)“leuk” is – omdat het altijd de standaardcodering van het platform gebruikt, wat bijna nooit is wat je wilt. Er is een overbelasting die de naam van de codering aanneemt, maar het gebruik van namen is geen goed idee IMO. Daarom hou ik van Charsets.*.

EDIT: Niet dat het bovenstaande een InputSupplier<InputStream>nodig heeft als de streamSupplier. Als je de stream al hebt, kun je dat echter gemakkelijk genoeg implementeren:

InputSupplier<InputStream> supplier = new InputSupplier<InputStream>() {
    @Override public InputStream getInput() {
        return stream;
    }
};

Antwoord 4, autoriteit 20%

UPDATE: als ik erop terugkijk, vind ik mijn oude oplossing niet goed. Bovendien is het nu 2013 en zijn er nu betere alternatieven beschikbaar voor Java7. Dus dit is wat ik nu gebruik:

InputStream fis = ...;
String text;
try (  InputStreamReader reader = new InputStreamReader(fis, Charsets.UTF_8)){
        text = CharStreams.toString(reader);
}

of indien met InputSupplier

InputSupplier<InputStreamReader> spl = ...
try (  InputStreamReader reader = spl.getInput()){
        text = CharStreams.toString(reader);
    }

Antwoord 5, autoriteit 14%

Een andere optie is om bytes uit Stream te lezen en er een String van te maken:

new String(ByteStreams.toByteArray(inputStream))
new String(ByteStreams.toByteArray(inputStream), Charsets.UTF_8)

Het is geen ‘pure’ guave, maar het is een beetje korter.


Antwoord 6, autoriteit 5%

Op basis van het geaccepteerde antwoord is hier een hulpprogramma-methode die de spot drijft met het gedrag van IOUtils.toString()(en ook een overbelaste versie met een tekenset). Deze versie zou toch veilig moeten zijn?

public static String toString(final InputStream is) throws IOException{
    return toString(is, Charsets.UTF_8);
}
public static String toString(final InputStream is, final Charset cs)
throws IOException{
    Closeable closeMe = is;
    try{
        final InputStreamReader isr = new InputStreamReader(is, cs);
        closeMe = isr;
        return CharStreams.toString(isr);
    } finally{
        Closeables.closeQuietly(closeMe);
    }
}

Antwoord 7, autoriteit 5%

Er is een veel kortere autoclosing-oplossing voor het geval de invoerstroom afkomstig is van de classpath-bron:

URL resource = classLoader.getResource(path);
byte[] bytes = Resources.toByteArray(resource);
String text = Resources.toString(resource, StandardCharsets.UTF_8);

Gebruikt Guava Bronnen, geïnspireerd door IOExplained.


Antwoord 8, autoriteit 2%

EDIT(2015): Okiois de beste abstractie en tools voor I/O in Java/Android die ik ken. Ik gebruik het de hele tijd.

FWIW dit is wat ik gebruik.

Als ik al een stream in de hand heb, dan:

final InputStream stream; // this is received from somewhere
String s = CharStreams.toString(CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
    public InputStream getInput() throws IOException {
        return stream;
    }
}, Charsets.UTF_8));

Als ik een stream aan het maken ben:

String s = CharStreams.toString(CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
    public InputStream getInput() throws IOException {
        return <expression creating the stream>;
    }
}, Charsets.UTF_8));

Als concreet voorbeeld kan ik een Android-tekstbestandsitem als volgt lezen:

final Context context = ...;
String s = CharStreams.toString(CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
    public InputStream getInput() throws IOException {
        return context.getAssets().open("my_asset.txt");
    }
}, Charsets.UTF_8));

Antwoord 9

Voor een concreet voorbeeld, hier is hoe ik een Android-tekstbestandsitem kan lezen:

public static String getAssetContent(Context context, String file) {
    InputStreamReader reader = null;
    InputStream stream = null;
    String output = "";
    try {
        stream = context.getAssets().open(file);
        reader = new InputStreamReader(stream, Charsets.UTF_8);
        output = CharStreams.toString(reader);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return output;
}

Other episodes