IOUtils.toString (InputStream) için Guava eşdeğeri


106

Apache Commons IO , bir String'i okumak için güzel bir IOUtils.toString () yöntemine sahiptirInputStream .

Apache Commons'tan ve Guava'dan uzaklaşmaya çalıştığım için : Guava'da bir eşdeğeri var mı? com.google.common.ioPaketteki tüm sınıflara baktım ve bu kadar basit bir şey bulamadım.

Düzenleme: Karakter kümeleriyle ilgili sorunları anlıyor ve takdir ediyorum. Tüm kaynaklarımın ASCII'de (evet, ASCII, ANSI vb. Değil) olduğunu bildiğim için bu durumda kodlama benim için bir sorun değil.


2
Karakter grupları hakkında: Bir kütüphanenin, Charsets.US_ASCII"eh, tahmin ettiğim karakter seti ne olursa olsun" demenize izin vermek yerine, hangi karakter grubuyla uğraştığınızı (örneğin ) bilmenizi istemeniz yine de iyidir. birçok insan bunu yapmaktan mutlu görünüyor. Özellikle Java, UTF-8 gibi mantıklı bir varsayılan kullanmadığından.
ColinD

Biliyorum. Bu yüzden kendi cevabımda UTF-8'i varsayılan sürüm olarak kullanıyorum.
Sean Patrick Floyd


@Vadzim bu soru sorulduğunda bu dokümanlar yoktu :-)
Sean Patrick Floyd

Yanıtlar:


85

Calum'un cevabına yorumunuzda kullanacağınızı belirtmişsiniz.

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

Bu kod sorunludur çünkü aşırı yük CharStreams.toString(Readable)durumu:

Kapatmaz Readable.

Bu, sizin InputStreamReaderve uzantıya göre InputStreamiade edilensupplier.get() bu kod tamamlandıktan sonra kapatılmayacağı .

Öte yandan, halihazırda bir InputSupplier<InputStream>aşırı yüklemeye sahip olduğunuz ve aşırı yüklemeyi kullandığınız gerçeğinden yararlanırsanız CharStreams.toString(InputSupplier<R extends Readable & Closeable>), toStringyöntem hem oluşturmayı hem de kapatmayı ele alacaktır.Reader sizin için .

Bu tam olarak Jon Skeet'in önerdiği şeydir, ancak aslında girdi olarak CharStreams.newReaderSupplieralan herhangi bir aşırı yükleme yoktur InputStream... ona bir vermelisiniz 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);

Buradaki InputSupplieramaç, Guava'nın try-finallykaynakların düzgün bir şekilde kapatılmasını sağlamak için çirkin bir blok gerektiren parçaları işlemesine izin vererek hayatınızı kolaylaştırmaktır .

Düzenleme: Şahsen, aşağıdakileri buluyorum (aslında bunu böyle yazıyordum, sadece yukarıdaki koddaki adımları parçalıyordu)

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

bundan çok daha az ayrıntılı olmak :

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

Bunu kendiniz halledebilmek için aşağı yukarı yazmanız gereken şey budur.


Düzenleme: Şubat 2014

InputSupplierve OutputSupplierbunları kullanan yöntemler Guava 16.0'da kullanımdan kaldırılmıştır. Onların yerine vardır ByteSource, CharSource, ByteSinkve CharSink. A verildiğinde ByteSource, artık içeriğini şu şekilde alabilirsiniz String:

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

Harika bilgi (+1) için teşekkürler. Ama bu çok ayrıntılı. Kabul edilen cevabı Closeables.closeQuietly () ile birleştirmenin daha kolay olduğunu düşünüyorum.
Sean Patrick Floyd

@CollinD: Ben bir göz benim answer.Please birinde yöntemini kullanmıştır kodu ve bu InputSupplier kullanmak doğru yolu olup olmadığını söyle.
Emil

1
@ColinD, inputStream bir doPost sunucu uygulamasının içinden geliyorsa, onu kapatmanın herhangi bir anlamı var mı? (veya kapatma konusunda endişelenerek)
Blankman

CharStreams.toString (InputSupplier) artık kullanımdan kaldırılmıştır. Bir CharSource oluşturdum (asCharSource kullanarak bir ByteSource'tan) ve ardından belgelerin önerdiği gibi toString'i kullandım.
John Lehmann

4
TedM.Young @: Eğer varsa hepsi bir ise InputStreamve bir şekilde bunu elde etmek istiyorum String, CharStreams.toString(new InputStreamReader(inputStream, charset))gitmek için bir yoldur. ByteSourceve CharSourceözellikle bir InputStreams veya Readers kaynağı olarak hareket edebilecek bir şeye sahip olduğunuz durumlar içindir .
ColinD

56

Biriniz varsa Readablekullanabilirsiniz CharStreams.toString(Readable). Yani muhtemelen aşağıdakileri yapabilirsiniz:

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

Sizi bir karakter seti belirtmeye zorlar, sanırım yine de yapmalısınız.


4
Aslında, sizin ve Jon Skeet'in yanıtlarının bir kombinasyonunu kullanacağım: `` CharStreams.toString (yeni InputStreamReader (Supplier.get (), Charsets.UTF_8)) ''
Sean Patrick Floyd

Evet, seçenekleri birleştirmenin birçok yolu!
Calum

10
@SPFloyd: Eğer bir tane varsa, yerine InputSupplier<InputStream>kullanmanızı şiddetle tavsiye ederim . Nedeni verildiğinde yani , irade değil o kadar yakın (temel akış ve böylece değil!). Bir for the kullanarak , yöntem sizin için kapatmayı halledecektir . CharStreams.newReaderSupplier(supplier, Charsets.UTF_8)new InputStreamReaderInputStreamReadertoStringReaderInputSupplierReadertoStringReader
ColinD

17

GÜNCELLEME : Geriye dönüp baktığımda eski çözümümü beğenmiyorum. Üstelik şu an 2013 ve Java7 için artık daha iyi alternatifler var. İşte şimdi kullandığım şey:

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

veya InputSupplier ile

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

16

Neredeyse. Bunun gibi bir şey kullanabilirsiniz:

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

Şahsen ben yok düşünüyorum IOUtils.toString(InputStream)her zaman neredeyse hiç ne istiyorsun platformu, varsayılan kodlamayı kullandığı için - "güzel" dir. Kodlamanın adını alan bir aşırı yükleme var, ancak adların kullanılması harika bir fikir değil IMO. Bu yüzden seviyorumCharsets.* .

DÜZENLEME: Yukarıdaki bir ihtiyacı yok o InputSupplier<InputStream>kadar streamSupplier. Akışı zaten aldıysanız, bunu yeterince kolayca uygulayabilirsiniz:

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

Jon, request.getInputStream aracılığıyla akış mı? Ayrıca, @ Calum'un cevabında bahsettiği ColinD gibi sizinki akışı kapatacak mı?
Blankman

Oh, ve bu bir servlet doPost ortamı, yine de akışı kapatmalı mıyım?
Blankman

@Blankman: Ah, bu sizin bağlamınız - sorunuz hiç de net değildi. Bir istek akışını kapatmanız çok önemli değil, ancak genellikle kapatırım. Yine de bu yanıtı düzenleyeceğim - öyle görünüyor ki böyle bir aşırı yük yok.
Jon Skeet

1
Bunu şimdi yapıyorum: String payLoad = CharStreams.toString (yeni InputStreamReader (request.getInputStream (), "UTF-8"));
Blankman

1
@BeeOnRope: Sanırım ara yaklaşımlardan biri Charsets.UTF_8.name()- daha fazla yazım hatası direnci.
Jon Skeet

11

Başka bir seçenek de Akıştan bayt okumak ve bunlardan bir Dize oluşturmaktır:

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

'Saf' Guava değil, ama biraz daha kısa.


ByteStreams.toByteArray()Javadoc'a göre maalesef dereyi kapatmıyor.
The Alchemist

Bu doğru. Akışı kapatan herhangi bir Guava işlevi görmedim. Yakın Sessizlik dışında.
ponomandr

1
Tipik olarak, akış kaynakları dene deyiminde açılır ve otomatik olarak kapatılır, bu nedenle, toByteArray ()
ponomandr

4

Kabul edilen yanıta dayalı olarak, burada davranışını IOUtils.toString()(ve ayrıca bir karakter kümesine sahip aşırı yüklenmiş bir sürümü) alay eden bir yardımcı program yöntemi verilmiştir . Bu sürüm güvenli olmalı, değil mi?

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

Bana oldukça iyi görünüyor. Guava'nın IO işleri, tek seferlik akışlar ve okuyucular (mümkün olduğunda) yerine yeniden kullanılabilir girdi tedarikçileri açısından düşünmeyi öğrenirseniz en iyi sonucu verir, ancak sanırım mevcut IOUtils kodunu dönüştürdüğünüz için bu büyük bir değişiklik olacaktır.
ColinD

2
Guava 14'ümde closeQuietly zaten kullanımdan kaldırıldı. Öneri, Java 7'de bulunan kaynakları dene özelliğini kullanmaktır. Bu
konuyla

2
@AlbertKam kabul etti. Ama unutmayın: bu cevap üç yaşında.
Sean Patrick Floyd

@SeanPatrickFloyd: Teşekkürler! Aslında cevabınızdan başlayarak yeni çözüme ulaştım. Yeni sürümü kullanıyor olabilecek diğerleri için yorum eklemeyi düşünüyordum. :)
bertie

4

Giriş akışının sınıf yolu kaynağından gelmesi durumunda çok daha kısa otomatik kapanma çözümü vardır:

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

IO Explained'dan esinlenerek Guava Kaynaklarını kullanır .


1
Bu soru sorulduğunda Kaynaklar sınıfı yoktu, ama haklısın: bugün muhtemelen gidilecek yol bu olurdu. Teşekkürler
Sean Patrick Floyd

2

DÜZENLEME (2015): Okio , Java / Android'de I / O için bildiğim en iyi soyutlama ve araçlardır. Ben her zaman kullanırım.

FWIW işte kullandığım şey

Elimde zaten bir akış varsa, o zaman:

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

Bir akış oluşturuyorsam:

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

Somut bir örnek olarak, aşağıdaki gibi bir Android metin dosyası varlığını okuyabilirim:

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

Artık hepsi kullanımdan kaldırıldı. :(
user3562927

1
Bunun yerine github.com/square/okio'yu deneyin - Guava'nın G / Ç'sini bir süredir kullanmadım, Okio daha iyi
orip

0

Somut bir örnek için, bir Android metin dosyası varlığını şu şekilde okuyabilirim:

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.