如何在 Java 中将 InputStream 读取/转换为字符串?

Posted

技术标签:

【中文标题】如何在 Java 中将 InputStream 读取/转换为字符串?【英文标题】:How do I read / convert an InputStream into a String in Java? 【发布时间】:2010-09-23 11:47:50 【问题描述】:

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


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

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

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

【问题讨论】:

这能回答你的问题吗? Scanner is skipping nextLine() after using next() or nextFoo()? 请记住,您需要考虑输入流的编码。系统默认值不一定总是你想要的。t 这些答案大部分是在 Java 9 之前编写的,但现在您可以使用 .readAllBytes 从 InputStream 获取字节数组。所以,简单的“new String(inputStream.readAllBytes())”使用 String 的 byte[] 构造函数。 【参考方案1】:

一个很好的方法是使用Apache commons@987654322@InputStream复制到StringWriter...类似

StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer, encoding);
String theString = writer.toString();

甚至

// NB: does not close inputStream, you'll have to use try-with-resources for that
String theString = IOUtils.toString(inputStream, encoding); 

如果您不想混合使用 Streams 和 Writer,也可以使用 ByteArrayOutputStream

【讨论】:

toString 是否被弃用?我看到IOUtils.convertStreamToString() 我添加了一个编辑以包含指向实际源代码本身的可搜索链接作为参考。我相信这为那些想了解该命令如何工作的人提供了更多答案。【参考方案2】:

考虑到文件,首先应该得到一个java.io.Reader 实例。然后可以读取它并将其添加到StringBuilder(如果我们不在多个线程中访问它,我们不需要StringBufferStringBuilder 更快)。这里的技巧是我们在块中工作,因此不需要其他缓冲流。块大小被参数化以优化运行时性能。

public static String slurp(final InputStream is, final int bufferSize) 
    final char[] buffer = new char[bufferSize];
    final StringBuilder out = new StringBuilder();
    try (Reader in = new InputStreamReader(is, "UTF-8")) 
        for (;;) 
            int rsz = in.read(buffer, 0, buffer.length);
            if (rsz < 0)
                break;
            out.append(buffer, 0, rsz);
        
    
    catch (UnsupportedEncodingException ex) 
        /* ... */
    
    catch (IOException ex) 
        /* ... */
    
    return out.toString();

【讨论】:

【参考方案3】:

Apache Commons 允许:

String myString = IOUtils.toString(myInputStream, "UTF-8");

当然,你可以选择除 UTF-8 之外的其他字符编码。

另见:(documentation)

【讨论】:

试图取回 InputStream,不工作***.com/q/66349701/3425489【参考方案4】:

用途:

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;

public static String readInputStreamAsString(InputStream in)
    throws IOException 

    BufferedInputStream bis = new BufferedInputStream(in);
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    int result = bis.read();
    while(result != -1) 
      byte b = (byte)result;
      buf.write(b);
      result = bis.read();
    
    return buf.toString();

【讨论】:

【参考方案5】:

如果您不能使用 Commons IO (FileUtils/IOUtils/CopyUtils),这里有一个使用 BufferedReader 逐行读取文件的示例:

public class StringFromFile 
    public static void main(String[] args) /*throws UnsupportedEncodingException*/ 
        InputStream is = StringFromFile.class.getResourceAsStream("file.txt");
        BufferedReader br = new BufferedReader(new InputStreamReader(is/*, "UTF-8"*/));
        final int CHARS_PER_PAGE = 5000; //counting spaces
        StringBuilder builder = new StringBuilder(CHARS_PER_PAGE);
        try 
            for(String line=br.readLine(); line!=null; line=br.readLine()) 
                builder.append(line);
                builder.append('\n');
            
         
        catch (IOException ignore)  

        String text = builder.toString();
        System.out.println(text);
    

或者,如果您想要原始速度,我会提出 Paul de Vrieze 建议的变体(避免使用 StringWriter(在内部使用 StringBuffer):

public class StringFromFileFast 
    public static void main(String[] args) /*throws UnsupportedEncodingException*/ 
        InputStream is = StringFromFileFast.class.getResourceAsStream("file.txt");
        InputStreamReader input = new InputStreamReader(is/*, "UTF-8"*/);
        final int CHARS_PER_PAGE = 5000; //counting spaces
        final char[] buffer = new char[CHARS_PER_PAGE];
        StringBuilder output = new StringBuilder(CHARS_PER_PAGE);
        try 
            for(int read = input.read(buffer, 0, buffer.length);
                    read != -1;
                    read = input.read(buffer, 0, buffer.length)) 
                output.append(buffer, 0, read);
            
         catch (IOException ignore)  

        String text = output.toString();
        System.out.println(text);
    

【讨论】:

【参考方案6】:

如果您使用的是 Google-Collections/Guava,您可以执行以下操作:

InputStream stream = ...
String content = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8));
Closeables.closeQuietly(stream);

请注意,InputStreamReader 的第二个参数(即 Charsets.UTF_8)不是必需的,但如果您知道编码,通常最好指定编码(您应该这样做!)

【讨论】:

【参考方案7】:

这里是只使用标准Java库的一种方式(注意流没有关闭,你的里程可能会有所不同)。

static String convertStreamToString(java.io.InputStream is) 
    java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
    return s.hasNext() ? s.next() : "";

我从"Stupid Scanner tricks" 文章中学到了这个技巧。它起作用的原因是因为Scanner 迭代流中的标记,在这种情况下,我们使用“输入边界的开始” (\A) 分隔标记,因此只为流的全部内容提供了一个标记。

注意,如果您需要具体说明输入流的编码,您可以向Scanner 构造函数提供第二个参数,该参数指示要使用的字符集(例如“UTF-8”)。

帽子提示也送给Jacob,他曾经向我指出过上述文章。

【讨论】:

我们不应该在返回值之前关闭扫描仪吗? @OlegMarkelov 可能。【参考方案8】:

用途:

InputStream in = /* Your InputStream */;
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String read;

while ((read=br.readLine()) != null) 
    //System.out.println(read);
    sb.append(read);


br.close();
return sb.toString();

【讨论】:

readLine() 删除换行符,因此生成的字符串将不包含换行符,除非您在添加到构建器的每一行之间添加行分隔符。【参考方案9】:

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

我尝试以 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%

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

【讨论】:

【参考方案10】:

如果您喜欢冒险,可以将 Scala 和 Java 混合使用,最终得到以下结果:

scala.io.Source.fromInputStream(is).mkString("")

混合使用 Java 和 Scala 代码和库有其好处。

在此处查看完整描述:Idiomatic way to convert an InputStream to a String in Scala

【讨论】:

【参考方案11】:

这里或多或少是 sampath 的答案,稍微整理一下并表示为一个函数:

String streamToString(InputStream in) throws IOException 
  StringBuilder out = new StringBuilder();
  BufferedReader br = new BufferedReader(new InputStreamReader(in));
  for(String line = br.readLine(); line != null; line = br.readLine()) 
    out.append(line);
  br.close();
  return out.toString();

【讨论】:

【参考方案12】:

这是最适合 android 和任何其他 JVM 的纯 Java 解决方案。

这个解决方案效果非常好......它简单、快速,并且在大小流上都一样! (参见上面的基准。No. 8

public String readFullyAsString(InputStream inputStream, String encoding)
        throws IOException 
    return readFully(inputStream).toString(encoding);


public byte[] readFullyAsBytes(InputStream inputStream)
        throws IOException 
    return readFully(inputStream).toByteArray();


private ByteArrayOutputStream readFully(InputStream inputStream)
        throws IOException 
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length = 0;
    while ((length = inputStream.read(buffer)) != -1) 
        baos.write(buffer, 0, length);
    
    return baos;

【讨论】:

【参考方案13】:

快速简单:

String result = (String)new ObjectInputStream( inputStream ).readObject();

【讨论】:

我收到java.io.StreamCorruptedException: invalid stream header ObjectInputStream 是关于反序列化,并且流必须尊重序列化协议才能工作,这在这个问题的上下文中可能并不总是正确的。 解释一下。【参考方案14】:

下面的代码对我有用。

URL url = MyClass.class.getResource("/" + configFileName);
BufferedInputStream bi = (BufferedInputStream) url.getContent();
byte[] buffer = new byte[bi.available() ];
int bytesRead = bi.read(buffer);
String out = new String(buffer);

请注意,根据 Java 文档,available() 方法可能不适用于 InputStream,但始终适用于 BufferedInputStream。 如果您不想使用available() 方法,我们可以随时使用以下代码

URL url = MyClass.class.getResource("/" + configFileName);
BufferedInputStream bi = (BufferedInputStream) url.getContent();
File f = new File(url.getPath());
byte[] buffer = new byte[ (int) f.length()];
int bytesRead = bi.read(buffer);
String out = new String(buffer);

我不确定是否会有任何编码问题。如果代码有任何问题,请发表评论。

【讨论】:

【参考方案15】:

以下是使用字节数组缓冲区仅使用 JDK 的方法。这实际上是 commons-io IOUtils.copy() 方法的全部工作方式。如果您从 Reader 复制而不是 InputStream,则可以将 byte[] 替换为 char[]

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

...

InputStream is = ....
ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
byte[] buffer = new byte[8192];
int count = 0;
try 
  while ((count = is.read(buffer)) != -1) 
    baos.write(buffer, 0, count);
  

finally 
  try 
    is.close();
  
  catch (Exception ignore) 
  


String charset = "UTF-8";
String inputStreamAsString = baos.toString(charset);

【讨论】:

【参考方案16】:

如果您使用流阅读器,请确保在最后关闭流

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


编辑:在 JDK 7+ 上,您可以使用 try-with-resources 构造。

/**
 * 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();
    

【讨论】:

【参考方案17】:

这是我经过一些实验后想出的最优雅的纯 Java(无库)解决方案:

public static String fromStream(InputStream in) throws IOException

    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    StringBuilder out = new StringBuilder();
    String newLine = System.getProperty("line.separator");
    String line;
    while ((line = reader.readLine()) != null) 
        out.append(line);
        out.append(newLine);
    
    return out.toString();

【讨论】:

【参考方案18】:

嗯,你可以自己编程……并不复杂……

String Inputstream2String (InputStream is) throws IOException
    
        final int PKG_SIZE = 1024;
        byte[] data = new byte [PKG_SIZE];
        StringBuilder buffer = new StringBuilder(PKG_SIZE * 10);
        int size;

        size = is.read(data, 0, data.length);
        while (size > 0)
        
            String str = new String(data, 0, size);
            buffer.append(str);
            size = is.read(data, 0, data.length);
        
        return buffer.toString();
    

【讨论】:

由于您在本地使用 buffer 变量,并且没有机会在多个线程之间共享,您应该考虑将其类型更改为 StringBuilder,以避免(无用)同步的开销。跨度> 亚历克斯说得好!我认为我们都同意这种方法在很多方面都不是线程安全的。甚至输入流操作也不是线程安全的。 如果流包含跨越多行的 UTF-8 字符,该算法可以将字符一分为二,打断字符串。 @VladLifliand 一个 UTF-8 字符究竟是如何跨越多行的?根据定义,这是不可能的。你可能还有别的意思。 @ChristianHujer 他的意思可能是buffers 而不是lines。 UTF-8 代码点/字符可以是多字节的。【参考方案19】:
  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);

【讨论】:

在 HarryLime 的回答中查看 ChristofferHammarström 的 commet。 问题中没有任何内容可以远程建议转换为哪个字符集,或者解决方案应该不受任何字符集的影响。 解释一下。【参考方案20】:
InputStreamReader i = new InputStreamReader(s);
BufferedReader str = new BufferedReader(i);
String msg = str.readLine();
System.out.println(msg);

这里是你的 InputStream 对象,它将被转换为 String

【讨论】:

如果在do-while循环中插入最后两行,它会起作用吗? 关于行的问题中没有任何内容。【参考方案21】:

JDK 7/8 的答案,它关闭了流并仍然抛出 IOException:

StringBuilder build = new StringBuilder();
byte[] buf = new byte[1024];
int length;
try (InputStream is = getInputStream()) 
  while ((length = is.read(buf)) != -1) 
    build.append(new String(buf, 0, length));
  

【讨论】:

【参考方案22】:

您可以使用 Apache Commons。

在 IOUtils 中,您可以找到具有三个有用实现的 toString 方法。

public static String toString(InputStream input) throws IOException 
        return toString(input, Charset.defaultCharset());


public static String toString(InputStream input) throws IOException 
        return toString(input, Charset.defaultCharset());


public static String toString(InputStream input, String encoding)
            throws IOException 
        return toString(input, Charsets.toCharset(encoding));

【讨论】:

前两种方法有什么区别?【参考方案23】:

试试这 4 个语句..

根据 Fred 回忆的要点,不建议将 String+= 运算符一起附加,因为每次将新的 char 附加到现有的 String 时都会再次创建新的 String 对象并将其地址分配给st,而旧的st 对象变成垃圾。

public String convertStreamToString(InputStream is)

    int k;
    StringBuffer sb=new StringBuffer();
    while((k=fin.read()) != -1)
    
        sb.append((char)k);
    
    return sb.toString();


不推荐,但这也是一种方式

public String convertStreamToString(InputStream is)  
    int k;
    String st="";
    while((k=is.read()) != -1)
    
        st+=(char)k;
    
    return st;

【讨论】:

在循环中使用+= 运算符连接字符串不是一个好主意。最好使用StringBuilderStringBuffer【参考方案24】:

这个 sn-p 是在 \sdk\samples\android-19\connectivity\NetworkConnect\NetworkConnectSample\src\main\java\com\example\android\networkconnect\MainActivity.java 中找到的,它是在 Apache 许可证下获得许可的,版本2.0 并由 Google 编写。

/** Reads an InputStream and converts it to a String.
 * @param stream InputStream containing html from targeted site.
 * @param len Length of string that this method returns.
 * @return String concatenated according to len parameter.
 * @throws java.io.IOException
 * @throws java.io.UnsupportedEncodingException
 */
private String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException 
    Reader reader = null;
    reader = new InputStreamReader(stream, "UTF-8");
    char[] buffer = new char[len];
    reader.read(buffer);
    return new String(buffer);

【讨论】:

【参考方案25】:

我已经编写了一个可以做到这一点的课程,所以我想我会与大家分享。有时您不想仅仅为了一件事而添加 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);
        
    

【讨论】:

【参考方案26】:

这是在不使用任何第三方库的情况下将InputStream 转换为String 的完整方法。对单线程环境使用StringBuilder,否则使用StringBuffer

public static String getString( InputStream is) throws IOException 
    int ch;
    StringBuilder sb = new StringBuilder();
    while((ch = is.read()) != -1)
        sb.append((char)ch);
    return sb.toString();

【讨论】:

【参考方案27】:

我有 log4j 可用,所以我可以使用 org.apache.log4j.lf5.util.StreamUtils.getBytes 来获取字节,我可以使用 String ctor 将其转换为字符串

String result = new String(StreamUtils.getBytes(inputStream));

【讨论】:

-1。仅仅因为某些东西可用并不意味着它应该被使用。当您切换日志记录提供程序时,您将不得不替换它。此外,它看起来像是内部的,不应该真正在 log4j 之外使用。【参考方案28】:

这个很好,因为:

它可以安全地处理字符集。 您可以控制读取缓冲区的大小。 您可以设置构建器的长度,它不必是精确值。 没有库依赖。 适用于 Java 7 或更高版本。

怎么做?

public static String convertStreamToString(InputStream is) throws IOException 
   StringBuilder sb = new StringBuilder(2048); // Define a size if you have an idea of it.
   char[] read = new char[128]; // Your buffer size.
   try (InputStreamReader ir = new InputStreamReader(is, StandardCharsets.UTF_8)) 
     for (int i; -1 != (i = ir.read(read)); sb.append(read, 0, i));
   
   return sb.toString();

对于 JDK 9

public static String inputStreamString(InputStream inputStream) throws IOException 
    try (inputStream) 
        return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
    

【讨论】:

【参考方案29】:

我会使用一些 Java 8 技巧。

public static String streamToString(final InputStream inputStream) throws Exception 
    // buffering optional
    try
    (
        final BufferedReader br
           = new BufferedReader(new InputStreamReader(inputStream))
    ) 
        // parallel optional
        return br.lines().parallel().collect(Collectors.joining("\n"));
     catch (final IOException e) 
        throw new RuntimeException(e);
        // whatever.
    

除了更简洁之外,与其他一些答案基本相同。

【讨论】:

【参考方案30】:

这是改编自 org.apache.commons.io.IOUtils source code 的答案,适用于那些想要实现 apache 但不想要整个库的人。

private static final int BUFFER_SIZE = 4 * 1024;

public static String inputStreamToString(InputStream inputStream, String charsetName)
        throws IOException 
    StringBuilder builder = new StringBuilder();
    InputStreamReader reader = new InputStreamReader(inputStream, charsetName);
    char[] buffer = new char[BUFFER_SIZE];
    int length;
    while ((length = reader.read(buffer)) != -1) 
        builder.append(buffer, 0, length);
    
    return builder.toString();

【讨论】:

以上是关于如何在 Java 中将 InputStream 读取/转换为字符串?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Java 中将 InputStream 读取/转换为字符串?

如何在 Java 中将 InputStream 读取/转换为字符串?

如何在 Java 中将 InputStream 读取/转换为字符串?

如何在 Java 中将 InputStream 转换为字符串?

如何在 Java 中将 InputStream 转换为字符串?

如何在 Java ME 中将 StringBuffer 转换为 InputStream?