如果您有java.io.InputStream对象,应该如何处理该对象并生成String?


假设我有一个包含文本数据的InputStream,我想将其转换为String,例如,我可以将其写入日志文件。

获取InputStream并将其转换为字符串的最简单方法是什么?

public String convertStreamToString(InputStream is) {
// ???
}

当前回答

我做了一些计时测试,因为时间总是很重要的。

我试图以3种不同的方式将响应转换为字符串。(如下所示)为了可读性,我省略了try/catch块。

为了给出上下文,这是所有3种方法的前面代码:

   String response;
   String url = "www.blah.com/path?key=value";
   GetMethod method = new GetMethod(url);
   int status = client.executeMethod(method);

1)

 response = method.getResponseBodyAsString();

2)

InputStream resp = method.getResponseBodyAsStream();
InputStreamReader is=new InputStreamReader(resp);
BufferedReader br=new BufferedReader(is);
String read = null;
StringBuffer sb = new StringBuffer();
while((read = br.readLine()) != null) {
    sb.append(read);
}
response = sb.toString();

3)

InputStream iStream  = method.getResponseBodyAsStream();
StringWriter writer = new StringWriter();
IOUtils.copy(iStream, writer, "UTF-8");
response = writer.toString();

因此,在使用相同的请求/响应数据对每种方法运行了500次测试之后,以下是数字。再次,这些是我的发现,你的发现可能不完全相同,但我写这篇文章是为了向其他人说明这些方法的效率差异。

排名:方法#1进近#3-比#1慢2.6%2号进近——比1号进近慢4.3%

任何这些方法都是获取响应并从中创建字符串的适当解决方案。

其他回答

以下内容并没有回答最初的问题,而是回答了一些问题。

几个回答暗示了这种形式的循环

String line = null;
while((line = reader.readLine()) != null) {
  // ...
}

or

for(String line = reader.readLine(); line != null; line = reader.readLine()) {
    // ...
}

第一种形式通过在封闭作用域中声明一个变量“read”来污染封闭作用域的命名空间,该变量不会用于for循环之外的任何内容。第二个表单重复readline()调用。

这里有一种用Java编写这种循环的更简洁的方法。事实证明,for循环中的第一个子句不需要实际的初始值设定项值。这将变量“line”的范围保持在for循环的主体内。更优雅!我在任何地方都没有看到有人使用这个表单(几年前的一天我随机发现了它),但我一直在使用它。

for (String line; (line = reader.readLine()) != null; ) {
    //...
}

我已经写了一个这样的课,所以我想我会和大家分享。有时候,您不想仅仅为了一件事而添加Apache Commons,并且想要比Scanner更笨的东西,它不检查内容。

用法如下

// Read from InputStream
String data = new ReaderSink(inputStream, Charset.forName("UTF-8")).drain();

// Read from File
data = new ReaderSink(file, Charset.forName("UTF-8")).drain();

// Drain input stream to console
new ReaderSink(inputStream, Charset.forName("UTF-8")).drainTo(System.out);

以下是ReaderSink的代码:

import java.io.*;
import java.nio.charset.Charset;

/**
 * A simple sink class that drains a {@link Reader} to a {@link String} or
 * to a {@link Writer}.
 *
 * @author Ben Barkay
 * @version 2/20/2014
 */
public class ReaderSink {
    /**
     * The default buffer size to use if no buffer size was specified.
     */
    public static final int DEFAULT_BUFFER_SIZE = 1024;

    /**
     * The {@link Reader} that will be drained.
     */
    private final Reader in;

    /**
     * Constructs a new {@code ReaderSink} for the specified file and charset.
     * @param file      The file to read from.
     * @param charset   The charset to use.
     * @throws FileNotFoundException    If the file was not found on the filesystem.
     */
    public ReaderSink(File file, Charset charset) throws FileNotFoundException {
        this(new FileInputStream(file), charset);
    }

    /**
     * Constructs a new {@code ReaderSink} for the specified {@link InputStream}.
     * @param in        The {@link InputStream} to drain.
     * @param charset   The charset to use.
     */
    public ReaderSink(InputStream in, Charset charset) {
        this(new InputStreamReader(in, charset));
    }

    /**
     * Constructs a new {@code ReaderSink} for the specified {@link Reader}.
     * @param in    The reader to drain.
     */
    public ReaderSink(Reader in) {
        this.in = in;
    }

    /**
     * Drains the data from the underlying {@link Reader}, returning a {@link String} containing
     * all of the read information. This method will use {@link #DEFAULT_BUFFER_SIZE} for
     * its buffer size.
     * @return  A {@link String} containing all of the information that was read.
     */
    public String drain() throws IOException {
        return drain(DEFAULT_BUFFER_SIZE);
    }

    /**
     * Drains the data from the underlying {@link Reader}, returning a {@link String} containing
     * all of the read information.
     * @param bufferSize    The size of the buffer to use when reading.
     * @return  A {@link String} containing all of the information that was read.
     */
    public String drain(int bufferSize) throws IOException {
        StringWriter stringWriter = new StringWriter();
        drainTo(stringWriter, bufferSize);
        return stringWriter.toString();
    }

    /**
     * Drains the data from the underlying {@link Reader}, writing it to the
     * specified {@link Writer}. This method will use {@link #DEFAULT_BUFFER_SIZE} for
     * its buffer size.
     * @param out   The {@link Writer} to write to.
     */
    public void drainTo(Writer out) throws IOException {
        drainTo(out, DEFAULT_BUFFER_SIZE);
    }

    /**
     * Drains the data from the underlying {@link Reader}, writing it to the
     * specified {@link Writer}.
     * @param out           The {@link Writer} to write to.
     * @param bufferSize    The size of the buffer to use when reader.
     */
    public void drainTo(Writer out, int bufferSize) throws IOException {
        char[] buffer = new char[bufferSize];
        int read;
        while ((read = in.read(buffer)) > -1) {
            out.write(buffer, 0, read);
        }
    }
}
  InputStream IS=new URL("http://www.petrol.si/api/gas_prices.json").openStream();   

  ByteArrayOutputStream BAOS=new ByteArrayOutputStream();
  IOUtils.copy(IS, BAOS);
  String d= new String(BAOS.toByteArray(),"UTF-8");           

System.out.println(d);

如果使用流读取器,请确保在结束时关闭流

private String readStream(InputStream iStream) throws IOException {
    //build a Stream Reader, it can read char by char
    InputStreamReader iStreamReader = new InputStreamReader(iStream);
    //build a buffered Reader, so that i can read whole line at once
    BufferedReader bReader = new BufferedReader(iStreamReader);
    String line = null;
    StringBuilder builder = new StringBuilder();
    while((line = bReader.readLine()) != null) {  //Read till end
        builder.append(line);
        builder.append("\n"); // append new line to preserve lines
    }
    bReader.close();         //close all opened stuff
    iStreamReader.close();
    //iStream.close(); //EDIT: Let the creator of the stream close it!
                       // some readers may auto close the inner stream
    return builder.toString();
}

编辑:在JDK7+上,可以使用trywithresources构造。

/**
 * Reads the stream into a string
 * @param iStream the input stream
 * @return the string read from the stream
 * @throws IOException when an IO error occurs
 */
private String readStream(InputStream iStream) throws IOException {

    //Buffered reader allows us to read line by line
    try (BufferedReader bReader =
                 new BufferedReader(new InputStreamReader(iStream))){
        StringBuilder builder = new StringBuilder();
        String line;
        while((line = bReader.readLine()) != null) {  //Read till end
            builder.append(line);
            builder.append("\n"); // append new line to preserve lines
        }
        return builder.toString();
    }
}

使用java 9中支持的java.io.InputStream.transferTo(OutputStream)和ByteArrayOutputStream.toString(String),该字符串采用字符集名称:

public static String gobble(InputStream in, String charsetName) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    in.transferTo(bos);
    return bos.toString(charsetName);
}