Jambu biji setara untuk IOUtils.toString (InputStream)

106

Apache Commons IO memiliki metode kemudahan yang bagus IOUtils.toString () untuk membaca InputStreamke sebuah String.

Karena saya mencoba beralih dari Apache Commons dan ke Guava : apakah ada padanannya di Guava? Saya melihat semua kelas dalam com.google.common.iopaket dan saya tidak dapat menemukan sesuatu yang sesederhana itu.

Sunting: Saya memahami dan menghargai masalah dengan charsets. Kebetulan saya tahu bahwa semua sumber saya dalam ASCII (ya, ASCII, bukan ANSI dll.), Jadi dalam kasus ini, pengkodean bukan masalah bagi saya.

Sean Patrick Floyd
sumber
2
Tentang charsets: Masih bagus untuk library yang meminta Anda untuk menentukan bahwa Anda tahu charset apa yang Anda hadapi (misalnya Charsets.US_ASCII) daripada membiarkan Anda mengatakan "eh, charset apa yang saya kira?" yang bagi banyak orang tampaknya senang melakukannya. Terutama karena Java tidak menggunakan default yang masuk akal, seperti UTF-8.
ColinD
Aku tahu. Itulah mengapa saya menggunakan UTF-8 sebagai versi default dalam jawaban saya sendiri.
Sean Patrick Floyd
Lihat juga
dokumennya
@Vadzim dokumen-dokumen itu tidak ada ketika pertanyaan ini diajukan :-)
Sean Patrick Floyd

Jawaban:

85

Anda menyatakan dalam komentar Anda atas jawaban Calum yang akan Anda gunakan

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

Kode ini bermasalah karena kelebihan beban CharStreams.toString(Readable)menyatakan:

Tidak menutup Readable.

Ini berarti Anda InputStreamReader, dan dengan ekstensi yang InputStreamdikembalikan oleh supplier.get(), tidak akan ditutup setelah kode ini selesai.

Sebaliknya, jika Anda memanfaatkan fakta bahwa Anda tampaknya sudah memiliki InputSupplier<InputStream>dan menggunakan kelebihan beban CharStreams.toString(InputSupplier<R extends Readable & Closeable>), toStringmetode ini akan menangani pembuatan dan penutupan Readeruntuk Anda.

Ini persis seperti yang disarankan Jon Skeet, kecuali bahwa sebenarnya tidak ada kelebihan beban CharStreams.newReaderSupplieryang membutuhkan InputStreammasukan ... Anda harus memberikannya 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);

Intinya InputSupplieradalah untuk membuat hidup Anda lebih mudah dengan mengizinkan Jambu untuk menangani bagian-bagian yang membutuhkan try-finallyblok jelek untuk memastikan bahwa sumber daya ditutup dengan benar.

Sunting: Secara pribadi, saya menemukan yang berikut (begitulah sebenarnya saya menulisnya, baru saja memecah langkah-langkah dalam kode di atas)

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

menjadi jauh lebih verbose dari ini:

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

Kurang lebih apa yang harus Anda tulis untuk menangani ini sendiri dengan benar.


Edit: Februari 2014

InputSupplierdan OutputSupplierdan metode yang menggunakannya sudah tidak digunakan lagi di Guava 16.0. Pengganti mereka ByteSource, CharSource, ByteSinkdan CharSink. Diberikan ByteSource, Anda sekarang bisa mendapatkan isinya Stringseperti ini:

ByteSource source = ...
String text = source.asCharSource(Charsets.UTF_8).read();
ColinD
sumber
Terima kasih atas infonya yang luar biasa (+1). Tapi ini sangat bertele-tele. Saya pikir menggabungkan jawaban yang diterima dengan Closeables.closeQuietly () lebih mudah.
Sean Patrick Floyd
@CollinD: Saya telah menggunakan metode Anda di salah satu jawaban saya. Silakan lihat kodenya dan beri tahu saya apakah ini cara yang tepat untuk menggunakan InputSupplier.
Emil
1
@ ColinD, jika inputStream datang dari dalam servlet doPost, apakah ada gunanya menutupnya? (atau khawatir tentang menutupnya)
Blankman
CharStreams.toString (InputSupplier) sekarang tidak digunakan lagi. Saya membuat CharSource (dari ByteSource menggunakan asCharSource) lalu menggunakan toStringnya seperti yang disarankan oleh dokumen.
John Lehmann
4
@ TedM.Young: Jika semua yang Anda miliki adalah InputStream, dan Anda ingin mendapatkannya sebagai String, CharStreams.toString(new InputStreamReader(inputStream, charset))adalah caranya. ByteSourcedan CharSourcesecara khusus untuk kasus di mana Anda memiliki sesuatu yang dapat bertindak sebagai sumber InputStreams atau Readers.
ColinD
56

Jika Anda punya, ReadableAnda bisa menggunakan CharStreams.toString(Readable). Jadi, Anda mungkin dapat melakukan hal berikut:

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

Memaksa Anda untuk menentukan himpunan karakter, yang menurut saya harus Anda lakukan.

Calum
sumber
4
Sebenarnya, saya akan menggunakan kombinasi jawaban Anda dan Jon Skeet: `` CharStreams.toString (InputStreamReader baru (supplier.get (), Charsets.UTF_8)) ``
Sean Patrick Floyd
Ya, banyak cara untuk menggabungkan opsi!
Calum
10
@SPFloyd: Jika Anda memiliki, InputSupplier<InputStream>saya sangat menyarankan untuk menggunakan CharStreams.newReaderSupplier(supplier, Charsets.UTF_8)daripada new InputStreamReader. Alasannya adalah bahwa ketika diberi InputStreamReader, tidaktoString akan menutup itu (dan dengan demikian bukan aliran yang mendasarinya!). Dengan menggunakan for the , metode akan menangani penutupan untuk Anda. ReaderInputSupplierReadertoStringReader
ColinD
17

UPDATE : Melihat ke belakang, saya tidak suka solusi lama saya. Selain itu sekarang sudah 2013 dan sekarang ada alternatif yang lebih baik untuk Java7. Jadi inilah yang saya gunakan sekarang:

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

atau jika dengan InputSupplier

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

Hampir. Anda bisa menggunakan sesuatu seperti ini:

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

Secara pribadi saya tidak berpikir itu IOUtils.toString(InputStream)"bagus" - karena selalu menggunakan pengkodean default platform, yang hampir tidak pernah Anda inginkan. Ada kelebihan beban yang mengambil nama penyandian, tetapi menggunakan nama bukanlah ide yang bagus IMO. Itu sebabnya saya suka Charsets.*.

EDIT: Bukan berarti di atas membutuhkan InputSupplier<InputStream>sebagai streamSupplier. Jika Anda sudah memiliki streaming, Anda dapat menerapkannya dengan cukup mudah:

InputSupplier<InputStream> supplier = new InputSupplier<InputStream>() {
    @Override public InputStream getInput() {
        return stream;
    }
};
Jon Skeet
sumber
Jon, apakah streaming melalui request.getInputStream? Juga, apakah aliran Anda akan menutup streaming seperti yang disebutkan ColinD dalam jawaban @ Calum?
Blankman
Oh, dan ini adalah lingkungan servlet doPost, haruskah saya menutup alirannya?
Blankman
@Blankman: Ah, jadi itu konteks Anda - sama sekali tidak jelas dari pertanyaan Anda. Tidak masalah apakah Anda menutup aliran permintaan, tetapi saya biasanya akan melakukannya. Saya akan mengedit jawaban ini - tampaknya tidak ada kelebihan beban seperti itu.
Jon Skeet
1
Saya hanya melakukan ini sekarang: String payLoad = CharStreams.toString (new InputStreamReader (request.getInputStream (), "UTF-8"));
Blankman
1
@BeeOnRope: Saya kira satu pendekatan menengah Charsets.UTF_8.name()- lebih tahan kesalahan ketik.
Jon Skeet
11

Pilihan lainnya adalah membaca byte dari Stream dan membuat String darinya:

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

Ini bukan Jambu Biji 'murni', tapi sedikit lebih pendek.

ponomandr.dll
sumber
Sayangnya, ByteStreams.toByteArray()tidak menutup aliran, menurut Javadoc.
The Alchemist
Itu benar. Saya belum melihat fungsi Guava yang menutup aliran. Nah, kecuali closeQuietly.
ponomandr
1
Biasanya, aliran dibuka dalam pernyataan coba-dengan-sumber daya dan ditutup secara otomatis, jadi seharusnya tidak menjadi tanggung jawab toByteArray ()
ponomandr
4

Berdasarkan jawaban yang diterima, berikut adalah metode utilitas yang meniru perilaku IOUtils.toString()(dan versi yang kelebihan beban dengan charset, juga). Versi ini seharusnya aman, bukan?

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);
    }
}
Sean Patrick Floyd
sumber
Terlihat cukup baik bagiku. Hal-hal IO Guava bekerja paling baik jika Anda belajar untuk berpikir dalam hal pemasok input yang dapat digunakan kembali daripada aliran dan pembaca 1-shot (jika memungkinkan), tetapi saya kira karena Anda mengubah kode IOUtils yang ada, itu akan menjadi perubahan besar.
ColinD
2
Di guava 14 saya, closeQuietly sudah tidak digunakan lagi. Sarannya adalah gunakan fitur coba-dengan-sumber yang ada di Java 7. Lebih lanjut tentang ini di code.google.com/p/guava-libraries/wiki/…
bertie
2
@Bertemu setuju. Tapi ingat: jawaban ini berumur tiga tahun.
Sean Patrick Floyd
@SeanPatrickFloyd: Terima kasih! Sebenarnya saya mendapat solusi yang lebih baru mulai dari jawaban Anda. Saya berpikir untuk menambahkan komentar untuk orang lain yang mungkin menggunakan versi yang lebih baru. :)
bertie
4

Ada solusi penutupan otomatis yang jauh lebih pendek jika aliran input berasal dari resource classpath:

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

Menggunakan Sumber Jambu Biji , terinspirasi oleh IOExplained .

Vadzim
sumber
1
Kelas Resources tidak ada ketika pertanyaan ini diajukan, tetapi Anda benar: hari ini mungkin cara yang tepat. Terima kasih
Sean Patrick Floyd
2

EDIT (2015): Okio adalah abstraksi dan alat terbaik untuk I / O di Java / Android yang saya ketahui. Saya menggunakannya sepanjang waktu.

FWIW inilah yang saya gunakan.

Jika saya sudah memiliki aliran di tangan, maka:

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

Jika saya membuat aliran:

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

Sebagai contoh konkret, saya bisa membaca aset file teks Android seperti ini:

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));
orip
sumber
Semua sudah usang sekarang. :(
user3562927
1
Coba github.com/square/okio sebagai gantinya - Saya sudah lama tidak menggunakan I / O Guava, Okio lebih baik,
orip
0

Untuk contoh konkret, berikut ini cara saya membaca aset file teks Android:

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;
}
TruMan1
sumber