IOUtils.toString(InputStream) 的番石榴等价物

Posted

技术标签:

【中文标题】IOUtils.toString(InputStream) 的番石榴等价物【英文标题】:Guava equivalent for IOUtils.toString(InputStream) 【发布时间】:2011-05-10 07:08:19 【问题描述】:

Apache Commons IO 有一个很好的便捷方法 IOUtils.toString() 可以将 InputStream 读取到字符串。

由于我试图从 Apache Commons 转移到 Guava:在 Guava 中是否有等价物?我查看了com.google.common.io 包中的所有类,但找不到任何简单的东西。

编辑:我理解并理解字符集的问题。碰巧我知道我所有的来源都是 ASCII(是的,ASCII,不是 ANSI 等),所以在这种情况下,编码对我来说不是问题。

【问题讨论】:

关于字符集:对于一个库来说,要求你指定你知道你正在处理的字符集(例如Charsets.US_ASCII)而不是让你说“嗯,无论我使用什么字符集,这仍然很好猜测?”很多人似乎都乐于这样做。特别是因为 Java 不使用有意义的默认值,例如 UTF-8。 我知道。这就是为什么我在自己的答案中使用 UTF-8 作为默认版本。 另见文档:code.google.com/p/guava-libraries/wiki/IOExplained @Vadzim 提出这个问题时这些文档不存在:-) 【参考方案1】:

如果您有Readable,则可以使用CharStreams.toString(Readable)。因此,您可能可以执行以下操作:

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

强制你指定一个字符集,我猜你应该这样做。

【讨论】:

实际上,我将结合使用您和 Jon Skeet 的答案:` CharStreams.toString(new InputStreamReader(supplier.get(), Charsets.UTF_8))` 是的,有很多组合选项的方法! @S.P.Floyd:如果您有InputSupplier<InputStream>,我强烈建议您使用CharStreams.newReaderSupplier(supplier, Charsets.UTF_8) 而不是new InputStreamReader。原因是当给定InputStreamReader 时,toString不会关闭Reader(因此不会关闭底层流!)。通过将InputSupplier 用于ReadertoString 方法将为您处理关闭Reader【参考方案2】:

几乎。你可以使用这样的东西:

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

我个人认为IOUtils.toString(InputStream) 是“不错的” - 因为它总是使用平台的默认编码,这几乎不是你想要的。有一个采用编码名称的重载,但使用名称不是一个好主意IMO。这就是我喜欢Charsets.*的原因。

编辑:并不是说上面需要InputSupplier&lt;InputStream&gt; 作为streamSupplier。如果你已经有了流,你可以很容易地实现它:

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

【讨论】:

乔恩,是通过 request.getInputStream 流式传输的吗?另外,你会像@Calum 的回答中提到的 ColinD 那样关闭流吗? 哦,它是一个 servlet doPost 环境,我应该关闭流吗? @Blankman:啊,这就是你的背景——你的问题根本不清楚。是否关闭请求流并不重要,但我通常会这样做。不过我会编辑这个答案 - 似乎没有这样的超载。 我现在才这样做:String payLoad = CharStreams.toString(new InputStreamReader(request.getInputStream(), "UTF-8")); @BeeOnRope:我想一种中间方法是Charsets.UTF_8.name() - 更耐打字。【参考方案3】:

您在对 Calum 的回答的评论中声明您将使用

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

此代码有问题,因为重载CharStreams.toString(Readable) 状态:

不关闭Readable

这意味着您的InputStreamReader 以及supplier.get() 返回的扩展InputStream 在此代码完成后不会关闭。

另一方面,如果您利用了您似乎已经拥有InputSupplier&lt;InputStream&gt; 并使用了重载CharStreams.toString(InputSupplier&lt;R extends Readable &amp; Closeable&gt;),则toString 方法将处理@ 的创建和关闭987654331@给你。

这正是 Jon Skeet 所建议的,除了实际上没有任何将 InputStream 用作输入的 CharStreams.newReaderSupplier 重载...你必须给它一个 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 年 2 月

InputSupplierOutputSupplier 以及使用它们的方法在 Guava 16.0 中已被弃用。他们的替代品是ByteSourceCharSourceByteSinkCharSink。给定一个ByteSource,您现在可以像这样以String 的形式获取其内容:

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

【讨论】:

感谢您提供的重要信息 (+1)。但这非常冗长。我认为将接受的答案与 Closeables.closeQuietly() 结合起来更容易。 @CollinD:我在其中一个答案中使用了您的方法。请查看code 并告诉我这是否是使用 InputSupplier 的正确方法。 @ColinD,如果 inputStream 来自 doPost servlet 内部,那么关闭它有什么意义吗? (或担心关闭它) CharStreams.toString(InputSupplier) 现在已弃用。我创建了一个 CharSource(从一个使用 asCharSource 的 ByteSource),然后按照文档的建议使用它的 toString。 @TedM.Young:如果您只有一个InputStream,并且您想将其作为String 获得,那么CharStreams.toString(new InputStreamReader(inputStream, charset)) 是您的最佳选择。 ByteSourceCharSource 专门用于您拥有可以充当 InputStreams 或 Readers 来源的东西的情况。【参考方案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);
    

【讨论】:

对我来说看起来很好。如果您学会从可重用的输入供应商而不是一次性流和读取器(如果可能的话)的角度来思考,Guava 的 IO 东西效果最好,但我想因为您正在转换现有的 IOUtils 代码,这将是一个很大的变化。跨度> 在我的番石榴 14 中,closeQuietly 已被弃用。建议使用 Java 7 中存在的 try-with-resources 功能。更多信息请访问code.google.com/p/guava-libraries/wiki/… @AlbertKam 同意了。但请记住:这个答案是三年前的。 @SeanPatrickFloyd:谢谢!实际上,我从您的回答开始得到了更新的解决方案。我正在考虑为可能正在使用较新版本的其他人添加评论。 :)【参考方案5】:

更新:回想起来,我不喜欢我的旧解决方案。此外,现在是 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);
    

【讨论】:

【参考方案6】:

EDIT (2015):Okio 是我所知道的 Java/android 中最好的 I/O 抽象和工具。我一直在使用它。

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

【讨论】:

现在全部弃用。 :( 试试github.com/square/okio吧——我已经有一段时间没用过Guava的I/O了,Okio简直更好,【参考方案7】:

另一种选择是从 Stream 中读取字节并从中创建一个字符串:

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

它不是“纯”番石榴,但它更短一些。

【讨论】:

不幸的是,ByteStreams.toByteArray() 没有关闭流,根据 Javadoc。 确实如此。我还没有看到任何关闭流的番石榴功能。好吧,除了 closeQuietly。 通常,流在 try-with-resources 语句中打开并自动关闭,因此它不应该由 toByteArray() 负责【参考方案8】:

举个具体的例子,下面是我如何读取 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;

【讨论】:

【参考方案9】:

如果输入流来自类路径资源,自动关闭解决方案会更短:

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

使用 Guava Resources,灵感来自 IOExplained。

【讨论】:

当被问到这个问题时,Resources 类并不存在,但你是对的:今天这可能是要走的路。谢谢

以上是关于IOUtils.toString(InputStream) 的番石榴等价物的主要内容,如果未能解决你的问题,请参考以下文章

使用java NIO及高速缓冲区写入文件

Javascript验证-isEmpty()

Delphi中Unicode转中文

输出流FileWriter和FileInputStream,和一个可以处理中文的OutputStreamWriter,如果要对中文进行传输和写入用OutputStreamWriter和InputStr

实现字符串分隔功能的函数

不匹配的括号