Guava еквівалент IOUtils.toString (InputStream)


106

Apache Commons IO має приємний метод зручності IOUtils.toString () для читання InputStreamрядка.

Оскільки я намагаюся відійти від Apache Commons та до Guava : чи є еквівалент у Guava? Я переглянув усі класи в com.google.common.ioпакеті, і не зміг знайти щось майже так просто.

Редагувати: Я розумію і ціную проблеми із графіками. Так буває, що я знаю, що всі мої джерела знаходяться в ASCII (так, ASCII, а не ANSI тощо), тому в цьому випадку кодування не є проблемою для мене.


2
Про діаграми: Бібліотеці все ще добре вимагати, щоб ви вказали, що ви знаєте, з якою діаграмою ви маєте справу (напр. Charsets.US_ASCII), А не дозволяли вам сказати "так, що б я не нагадала?" що багатьом людям здається щасливим зробити. Тим більше, що Java не використовує сенсор, який має сенс, як UTF-8.
ColinD

Я знаю. Ось чому я використовую UTF-8 як версію за замовчуванням у власній відповіді.
Шон Патрік Флойд

Дивіться також документи: code.google.com/p/guava-libraries/wiki/IOExplained
Вадим

@Vadzim цих документів не існувало, коли було задано це питання :-)
Шон Патрік Флойд

Відповіді:


85

Ви сказали у своєму коментарі на відповідь Калума, що збираєтесь використовувати

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

Цей код є проблематичним, оскільки перевантаження CharStreams.toString(Readable):

Не закриває Readable.

Це означає, що ваш InputStreamReader, і після розширення, що InputStreamповертається supplier.get(), не буде закритий після завершення цього коду.

Якщо, з іншого боку, ви скористаєтесь тим, що, здається, у вас вже є InputSupplier<InputStream>і використовували перевантаження CharStreams.toString(InputSupplier<R extends Readable & Closeable>), toStringметод буде обробляти як створення, так і закриття Readerдля вас.

Це саме те, що запропонував Джон Скіт, за винятком того, що насправді немає перевантаження того, CharStreams.newReaderSupplierщо приймається InputStreamяк вхід ... Ви повинні надати йому 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);

Сенс InputSupplierполягає в тому, щоб полегшити ваше життя, дозволяючи Guava обробляти деталі, які потребують некрасивого try-finallyблоку, щоб забезпечити належне закриття ресурсів.

Редагувати: Особисто я знаходжу наступне (саме так я насправді це написав, просто розбиваючи кроки в коді вище)

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

бути набагато менш багатослівним, ніж це:

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

Що більш-менш те, що вам доведеться написати, щоб самостійно впоратися з цим.


Редагувати: Лютий 2014

InputSupplierі OutputSupplierметоди, які їх використовують, були застаріли в Guava 16.0. Їх заміни є ByteSource, CharSource, ByteSinkі CharSink. З огляду на ByteSource, тепер ви можете отримати його вміст так String:

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

Дякуємо за чудову інформацію (+1). Але це дуже багатослівно. Я думаю, що поєднувати прийняту відповідь із Closeables.closeQuietly () простіше.
Шон Патрік Флойд

@CollinD: Я використовував ваш метод в одному зі своїх відповідей. Погляньте на код і скажіть мені, чи це правильний спосіб використання InputSupplier.
Еміль

1
@ColinD, якщо inputStream надходить зсередини сервлета doPost, чи є сенс його закрити? (або турбуючись про його закриття)
Бланкмен

CharStreams.toString (InputSupplier) тепер застарілий. Я створив CharSource (з ByteSource, використовуючи asCharSource), потім використовував його toString, як пропонують документи.
Джон Леманн

4
@ TedM.Young: Якщо все, що у вас є InputStream, і ви хочете отримати його як " String, CharStreams.toString(new InputStreamReader(inputStream, charset))- це шлях". ByteSourceі CharSourceспеціально для тих випадків, коли у вас є щось, що може виступати джерелом InputStreams або Readers.
ColinD

56

Якщо у вас є Readableви можете використовувати CharStreams.toString(Readable). Тож ви, ймовірно, можете зробити наступне:

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

Змушує вас вказати набір символів, який, напевно, ви повинні робити все одно.


4
Насправді я використовую комбінацію ваших відповідей та Джон Скіта: `CharStreams.toString (новий InputStreamReader (постачальник.get (), Charsets.UTF_8))`
Шон Патрік Флойд

Так, багато способів комбінувати варіанти!
Calum

10
@SPFloyd: Якщо у вас InputSupplier<InputStream>я настійно рекомендую використовувати, CharStreams.newReaderSupplier(supplier, Charsets.UTF_8)а не new InputStreamReader. Причина полягає в тому, що , коли , з огляду на InputStreamReader, toStringбуде НЕ близько , що Reader(і , отже , не основний потік!). Використовуючи для InputSupplierдля Reader, toStringметод буде обробляти закриття Readerдля вас.
ColinD

17

ОНОВЛЕННЯ : Озираючись назад, мені не подобається моє старе рішення. Крім того, зараз 2013 рік, і для Java7 є кращі альтернативи. Отже ось що я зараз використовую:

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

або якщо з InputSupplier

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

16

Близько. Ви можете використовувати щось подібне:

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

Особисто я не думаю, що IOUtils.toString(InputStream)це "приємно" - адже воно завжди використовує кодування за замовчуванням платформи, що майже ніколи не є тим, що ви хочете. Існує перевантаження, яка приймає назву кодування, але використання імен не є чудовою ідеєю IMO. Ось чому мені подобається Charsets.*.

EDIT: Не те, що вищезазначене потребує InputSupplier<InputStream>як streamSupplier. Якщо ви вже отримали потік, ви можете це легко реалізувати:

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

Джон, це потік через request.getInputStream? Також, чи закриє ваш потік, як ColinD, згаданий у відповіді @ Calum?
Бланкмен

О, і це сервлет-сервер doPost, я повинен як-небудь закривати потік?
Бланкмен

@Blankman: А, так це ваш контекст - це зовсім не було зрозуміло з вашого запитання. Не надто важливо, чи закриваєте ви потік запитів, але я, як правило, так роблю. Я відредагую цю відповідь, хоча - немає такої перевантаження, здається.
Джон Скіт

1
Я зараз це роблю: String payLoad = CharStreams.toString (новий InputStreamReader (request.getInputStream (), "UTF-8"));
Бланкмен

1
@BeeOnRope: Я думаю, один проміжний підхід Charsets.UTF_8.name()- більш стійкий до друку .
Джон Скіт

11

Ще один варіант - читати байти з Stream та створювати з них String:

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

Це не "чиста" Гуава, але трохи коротша.


На жаль, ByteStreams.toByteArray()не закриває потік, на думку Javadoc.
Алхімік

Це правда. Я не бачив жодної функції Guava, яка б закривала потік. Ну, хіба що тихо тихо.
ponomandr

1
Зазвичай потік відкривається в операторі спробу використання ресурсів і автоматично закривається, тому він не повинен відповідати toByteArray ()
ponomandr

4

Виходячи з прийнятої відповіді, тут є корисний метод, який висміює поведінку IOUtils.toString()(і перевантаженої версії, також із схемою). Ця версія повинна бути безпечною, правда?

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

Мені це виглядає дуже добре. IO-матеріали Guava спрацьовують найкраще, якщо ви навчитесь думати з точки зору багаторазових постачальників введення, а не однопотокових потоків та читачів (коли це можливо), але, мабуть, оскільки ви перетворюєте існуючий код IOUtils, це було б великою зміною.
ColinD

2
У моїй гуаві 14, тісно тихо вже застаріло. Пропонуємо скористатися функцією
пробного

2
@AlbertKam погодився. Але пам’ятайте: цій відповіді три роки.
Шон Патрік Флойд

@SeanPatrickFloyd: Дякую! Насправді я дійшов до нового рішення, починаючи з вашої відповіді. Я думав додати коментар для інших, хто може використовувати нову версію. :)
bertie

4

Існує набагато коротше рішення для автоматичного закриття у випадку, коли вхідний потік надходить з ресурсу classpath:

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

Використовує ресурси Guava , натхненні IOExplained .


1
Класу Ресурси не існувало, коли було задано це запитання, але ви праві: сьогодні, мабуть, це було б шляхом. Спасибі
Шон Патрік Флойд

2

EDIT (2015): Окіо - найкраща абстракція та інструменти для вводу-виводу в Java / Android, про які я знаю. Я ним користуюся постійно.

FWIW ось що я використовую.

Якщо у мене вже є потік у руці, то:

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

Якщо я створюю потік:

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

Як конкретний приклад, я можу прочитати такий елемент текстового файлу Android:

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

Зараз усі застарілі. :(
user3562927

1
Спробуйте замість github.com/square/okio - я вже не використовував введення / виведення Guava в даний час, Окіо просто краще,
відкрийте

0

Для конкретного прикладу, ось як я можу прочитати ресурс текстового файлу 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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.