java - 読み込み - string outputstream 変換




JavaでInputStreamをStringに変換する方法 (20)

java.io.InputStreamオブジェクトがある場合、そのオブジェクトをどのように処理してStringを生成する必要がありますか?

テキストデータを含むInputStreamがあり、それをStringに変換したいとします。そのため、たとえばログファイルに書き込むことができます。

InputStreamString変換する最も簡単な方法は何ですか?

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

Apache Commonsでは次のことが可能です。

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

もちろん、UTF-8以外の文字エンコーディングを選択することもできます。

参照:( Docs


Google-Collections / Guavaを使用している場合は、次の操作を行うことができます:

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

InputStreamReaderの2番目のパラメータ(Charsets.UTF_8)は必要ではないことに注意してください。しかし、それを知っていればエンコーディングを指定することはお勧めです


Guavaは、入力ストリームがクラスパスリソースから来た場合(これは一般的な作業のようです)には、効率の良い自動クローニングソリューションを提供します

byte[] bytes = Resources.toByteArray(classLoader.getResource(path));

または

String text = Resources.toString(classLoader.getResource(path), StandardCharsets.UTF_8);

ByteSourceCharSource一般的な概念もあり、ストリームの開始と終了の両方を静かに処理します。

例えば、小さなファイルを明示的に開いてその内容を読むのではなく、

String content = Files.asCharSource(new File("robots.txt"), StandardCharsets.UTF_8).read();
byte[] data = Files.asByteSource(new File("favicon.ico")).read();

あるいは単に

String content = Files.toString(new File("robots.txt"), StandardCharsets.UTF_8);
byte[] data = Files.toByteArray(new File("favicon.ico"));

Java 9のソリューションは完全なものです:

public static String toString(InputStream input) throws IOException {
    return new String(input.readAllBytes(), StandardCharsets.UTF_8);
}

readAllBytesは現在JDK 9のメインコードベースにあるため、リリースに表示される可能性があります。 JDK 9のスナップショットビルドを使用して今すぐ試すことができます


JDKの最も簡単な方法は、次のコードスニペットを使用する方法です。

String convertToString(InputStream in){
    String resource = new Scanner(in).useDelimiter("\\Z").next();
    return resource;
}

Kotlinのユーザーは単純に次の操作を行います。

println(InputStreamReader(is).readText())

一方、

readText()

Kotlin標準ライブラリの組み込み拡張メソッドです。


いくつかの実験の後に私が思いついたもっともエレガントで純粋な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();
}

ここには多かれ少なかれサンプンパスの答えがありますが、ちょっときれいにまとめられ、関数として表現されています:

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

これはどう?

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

これは私の純粋なJava&Androidソリューションで、うまくいきます...

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

どのように:

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

もう1つ、Springユーザー全員:

import java.nio.charset.StandardCharsets;
import org.springframework.util.FileCopyUtils;

public String convertStreamToString(InputStream is) throws IOException { 
    return new String(FileCopyUtils.copyToByteArray(is), StandardCharsets.UTF_8);
}

inのユーティリティメソッドはinのorg.springframework.util.StreamUtilsものと似ていますがFileCopyUtils、完了したらストリームを開いたままにします。


ファイルを考慮に入れて、まずjava.io.Readerインスタンスを取得する必要があります。 これを読み取ってStringBuilder追加することができます(複数のスレッドでアクセスしない場合はStringBufferは不要で、 StringBuilderは高速です)。 ここでのトリックは、ブロック単位で作業するため、他のバッファリングストリームを必要としないということです。 ブロックサイズは、実行時パフォーマンスの最適化のためにパラメータ化されています。

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

他の回答を要約するこれを行う11の主な方法を見つけました(下記参照)。 そして私はいくつかのパフォーマンステストを書いた(下記の結果を参照):

InputStreamをStringに変換する方法:

  1. IOUtils.toString使用(Apache Utils)

    String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
    
  2. CharStreams使用(Guava)

    String result = CharStreams.toString(new InputStreamReader(
          inputStream, Charsets.UTF_8));
    
  3. Scanner (JDK)の使用

    Scanner s = new Scanner(inputStream).useDelimiter("\\A");
    String result = s.hasNext() ? s.next() : "";
    
  4. ストリームAPI (Java 8)の使用。 警告 :この解決策は、 \r\nような異なる改行を\n変換します。

    String result = new BufferedReader(new InputStreamReader(inputStream))
      .lines().collect(Collectors.joining("\n"));
    
  5. パラレルストリームAPI (Java 8)の使用。 警告 :この解決策は、 \r\nような異なる改行を\n変換します。

    String result = new BufferedReader(new InputStreamReader(inputStream)).lines()
       .parallel().collect(Collectors.joining("\n"));
    
  6. InputStreamReaderおよびStringBuilder (JDK)の使用

    final int bufferSize = 1024;
    final char[] buffer = new char[bufferSize];
    final StringBuilder out = new StringBuilder();
    Reader in = new InputStreamReader(inputStream, "UTF-8");
    for (; ; ) {
        int rsz = in.read(buffer, 0, buffer.length);
        if (rsz < 0)
            break;
        out.append(buffer, 0, rsz);
    }
    return out.toString();
    
  7. StringWriterIOUtils.copy使用(Apache Commons)

    StringWriter writer = new StringWriter();
    IOUtils.copy(inputStream, writer, "UTF-8");
    return writer.toString();
    
  8. ByteArrayOutputStreamおよびinputStream.read (JDK)の使用

    ByteArrayOutputStream result = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length;
    while ((length = inputStream.read(buffer)) != -1) {
        result.write(buffer, 0, length);
    }
    // StandardCharsets.UTF_8.name() > JDK 7
    return result.toString("UTF-8");
    
  9. BufferedReader (JDK)の使用。 警告:この解決策は、 \n\rなどの異なる改行をline.separatorシステムプロパティ(たとえば、Windowsでは "\ r \ n")に変換します。

    String newLine = System.getProperty("line.separator");
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    StringBuilder result = new StringBuilder();
    boolean flag = false;
    for (String line; (line = reader.readLine()) != null; ) {
        result.append(flag? newLine: "").append(line);
        flag = true;
    }
    return result.toString();
    
  10. BufferedInputStreamおよびByteArrayOutputStream (JDK)の使用

    BufferedInputStream bis = new BufferedInputStream(inputStream);
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    int result = bis.read();
    while(result != -1) {
        buf.write((byte) result);
        result = bis.read();
    }
    // StandardCharsets.UTF_8.name() > JDK 7
    return buf.toString("UTF-8");
    
  11. inputStream.read()StringBuilder (JDK)を使用しています。 警告 :このソリューションは、Unicodeに問題があります。たとえば、ロシア語のテキスト(Unicode以外のテキストでのみ正しく動作します)

    int ch;
    StringBuilder sb = new StringBuilder();
    while((ch = inputStream.read()) != -1)
        sb.append((char)ch);
    reset();
    return sb.toString();
    

警告

  1. ソリューション4,5および9は、異なる改行を1に変換します。

  2. ソリューション11はUnicodeテキストで正しく機能しません

パフォーマンステスト

小さなString (長さ= 175)、 github URL(モード=平均時間、システム= Linux、スコア1,343が最適です)のパフォーマンステスト:

              Benchmark                         Mode  Cnt   Score   Error  Units
 8. ByteArrayOutputStream and read (JDK)        avgt   10   1,343 ± 0,028  us/op
 6. InputStreamReader and StringBuilder (JDK)   avgt   10   6,980 ± 0,404  us/op
10. BufferedInputStream, ByteArrayOutputStream  avgt   10   7,437 ± 0,735  us/op
11. InputStream.read() and StringBuilder (JDK)  avgt   10   8,977 ± 0,328  us/op
 7. StringWriter and IOUtils.copy (Apache)      avgt   10  10,613 ± 0,599  us/op
 1. IOUtils.toString (Apache Utils)             avgt   10  10,605 ± 0,527  us/op
 3. Scanner (JDK)                               avgt   10  12,083 ± 0,293  us/op
 2. CharStreams (guava)                         avgt   10  12,999 ± 0,514  us/op
 4. Stream Api (Java 8)                         avgt   10  15,811 ± 0,605  us/op
 9. BufferedReader (JDK)                        avgt   10  16,038 ± 0,711  us/op
 5. parallel Stream Api (Java 8)                avgt   10  21,544 ± 0,583  us/op

大きなString (長さ= 50100)、 github URL(モード=平均時間、システム= Linux、スコア200,715が最適です)のパフォーマンステスト:

               Benchmark                        Mode  Cnt   Score        Error  Units
 8. ByteArrayOutputStream and read (JDK)        avgt   10   200,715 ±   18,103  us/op
 1. IOUtils.toString (Apache Utils)             avgt   10   300,019 ±    8,751  us/op
 6. InputStreamReader and StringBuilder (JDK)   avgt   10   347,616 ±  130,348  us/op
 7. StringWriter and IOUtils.copy (Apache)      avgt   10   352,791 ±  105,337  us/op
 2. CharStreams (guava)                         avgt   10   420,137 ±   59,877  us/op
 9. BufferedReader (JDK)                        avgt   10   632,028 ±   17,002  us/op
 5. parallel Stream Api (Java 8)                avgt   10   662,999 ±   46,199  us/op
 4. Stream Api (Java 8)                         avgt   10   701,269 ±   82,296  us/op
10. BufferedInputStream, ByteArrayOutputStream  avgt   10   740,837 ±    5,613  us/op
 3. Scanner (JDK)                               avgt   10   751,417 ±   62,026  us/op
11. InputStream.read() and StringBuilder (JDK)  avgt   10  2919,350 ± 1101,942  us/op

グラフ(Windows 7システムの入力ストリーム長に応じたパフォーマンステスト)

Windows 7システムの入力ストリームの長さに応じたパフォーマンステスト(平均時間):

 length  182    546     1092    3276    9828    29484   58968

 test8  0.38    0.938   1.868   4.448   13.412  36.459  72.708
 test4  2.362   3.609   5.573   12.769  40.74   81.415  159.864
 test5  3.881   5.075   6.904   14.123  50.258  129.937 166.162
 test9  2.237   3.493   5.422   11.977  45.98   89.336  177.39
 test6  1.261   2.12    4.38    10.698  31.821  86.106  186.636
 test7  1.601   2.391   3.646   8.367   38.196  110.221 211.016
 test1  1.529   2.381   3.527   8.411   40.551  105.16  212.573
 test3  3.035   3.934   8.606   20.858  61.571  118.744 235.428
 test2  3.136   6.238   10.508  33.48   43.532  118.044 239.481
 test10 1.593   4.736   7.527   20.557  59.856  162.907 323.147
 test11 3.913   11.506  23.26   68.644  207.591 600.444 1211.545

標準のJavaライブラリのみを使用する方法があります(ストリームは閉じられていないことに注意してください、YMMV)。

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

私は"愚かなスキャナーのトリック"記事からこのトリックを学びました。 その理由は、 Scannerがストリーム内のトークンを反復処理するためです。この場合、「入力境界の始まり」(\ A)を使用してトークンを区切り、ストリームの内容全体に対してトークンを1つだけ与えます。

入力ストリームのエンコーディングについて具体的にする必要がある場合は、使用する文字セット(たとえば "UTF-8")を示す2番目の引数をScannerコンストラクタにScannerことができます。

帽子の先端はJacob,も行きます。 Jacob,かつてこの記事に私を指摘しました。

EDITED: Patrickからの提案のおかげで、空の入力ストリームを扱うときに関数をより強固にしました。 もう1つの編集:不快な試み/キャッチ、パトリックの方法は不気味です。


私はいくつかの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.
    }
}

基本的には、より簡潔なものを除いて他のいくつかの答えと同じです。



ここに私のJava 8ベースのソリューションは、新しいストリームAPIを使用してすべての行を収集するInputStream

public static String toString(InputStream inputStream) {
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(inputStream));
    return reader.lines().collect(Collectors.joining(
        System.getProperty("line.separator")));
}

サードパーティ製のライブラリを使用せずに変換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();
}

バイト配列バッファを使用してJDKを使用する方法は次のとおりです。これは実際にコモンズIOUtils.copy()メソッドがどのように動作するかです。。の代わりにからコピーbyte[]しているchar[]場合は、と置き換えることができます。ReaderInputStream

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




inputstream