이란 - java text input stream




Java에서 InputStream을 String으로 읽거나 변환하는 방법은 무엇입니까? (20)

java.io.InputStream 객체가 있다면 그 객체를 어떻게 처리하고 String 생성해야합니까?

텍스트 데이터를 포함하는 InputStream 이 있다고 가정하고이를 String 으로 변환하려고합니다. 예를 들어이를 로그 파일에 쓸 수 있습니다.

InputStream 을 가져 와서 String 변환하는 가장 쉬운 방법은 무엇입니까?

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 의 두 번째 매개 변수 (예 : Charsets.UTF_8)는 필요하지 않지만, 알고 있다면 인코딩을 지정하는 것이 좋습니다.


나는 14 가지의 해답을 제시했다. (크레딧을 제공하지 못해 미안하지만 너무 많은 중복이있다)

결과는 매우 놀랍습니다. Apache IOUtils 가 가장 느리고 ByteArrayOutputStream 이 가장 빠른 솔루션입니다.

먼저 여기에 가장 좋은 방법이 있습니다.

public String inputStreamToString(InputStream inputStream) throws IOException {
    try(ByteArrayOutputStream result = new ByteArrayOutputStream()) {
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) != -1) {
            result.write(buffer, 0, length);
        }

        return result.toString(UTF_8);
    }
}

20 사이클의 20MB 임의 바이트의 벤치 마크 결과

밀리 초 단위의 시간

  • ByteArrayOutputStreamTest : 194
  • NioStream : 198
  • Java9ISTransferTo : 201
  • Java9ISReadAllBytes : 205
  • BufferedInputStreamVsByteArrayOutputStream : 314
  • ApacheStringWriter2 : 574
  • GuavaCharStreams : 589
  • ScannerReaderNoNextTest : 614
  • ScannerReader : 633
  • ApacheStringWriter : 1544
  • StreamApi : 오류
  • ParallelStreamApi : 오류
  • BufferReaderTest : 오류
  • InputStreamAndStringBuilder : 오류

벤치 마크 소스 코드

import com.google.common.io.CharStreams;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * Created by Ilya Gazman on 2/13/18.
 */
public class InputStreamToString {


    private static final String UTF_8 = "UTF-8";

    public static void main(String... args) {
        log("App started");
        byte[] bytes = new byte[1024 * 1024];
        new Random().nextBytes(bytes);
        log("Stream is ready\n");

        try {
            test(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void test(byte[] bytes) throws IOException {
        List<Stringify> tests = Arrays.asList(
                new ApacheStringWriter(),
                new ApacheStringWriter2(),
                new NioStream(),
                new ScannerReader(),
                new ScannerReaderNoNextTest(),
                new GuavaCharStreams(),
                new StreamApi(),
                new ParallelStreamApi(),
                new ByteArrayOutputStreamTest(),
                new BufferReaderTest(),
                new BufferedInputStreamVsByteArrayOutputStream(),
                new InputStreamAndStringBuilder(),
                new Java9ISTransferTo(),
                new Java9ISReadAllBytes()
        );

        String solution = new String(bytes, "UTF-8");

        for (Stringify test : tests) {
            try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
                String s = test.inputStreamToString(inputStream);
                if (!s.equals(solution)) {
                    log(test.name() + ": Error");
                    continue;
                }
            }
            long startTime = System.currentTimeMillis();
            for (int i = 0; i < 20; i++) {
                try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
                    test.inputStreamToString(inputStream);
                }
            }
            log(test.name() + ": " + (System.currentTimeMillis() - startTime));
        }
    }

    private static void log(String message) {
        System.out.println(message);
    }

    interface Stringify {
        String inputStreamToString(InputStream inputStream) throws IOException;

        default String name() {
            return this.getClass().getSimpleName();
        }
    }

    static class ApacheStringWriter implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            StringWriter writer = new StringWriter();
            IOUtils.copy(inputStream, writer, UTF_8);
            return writer.toString();
        }
    }

    static class ApacheStringWriter2 implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return IOUtils.toString(inputStream, UTF_8);
        }
    }

    static class NioStream implements Stringify {

        @Override
        public String inputStreamToString(InputStream in) throws IOException {
            ReadableByteChannel channel = Channels.newChannel(in);
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 16);
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            WritableByteChannel outChannel = Channels.newChannel(bout);
            while (channel.read(byteBuffer) > 0 || byteBuffer.position() > 0) {
                byteBuffer.flip();  //make buffer ready for write
                outChannel.write(byteBuffer);
                byteBuffer.compact(); //make buffer ready for reading
            }
            channel.close();
            outChannel.close();
            return bout.toString(UTF_8);
        }
    }

    static class ScannerReader implements Stringify {

        @Override
        public String inputStreamToString(InputStream is) throws IOException {
            java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
            return s.hasNext() ? s.next() : "";
        }
    }

    static class ScannerReaderNoNextTest implements Stringify {

        @Override
        public String inputStreamToString(InputStream is) throws IOException {
            java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
            return s.next();
        }
    }

    static class GuavaCharStreams implements Stringify {

        @Override
        public String inputStreamToString(InputStream is) throws IOException {
            return CharStreams.toString(new InputStreamReader(
                    is, UTF_8));
        }
    }

    static class StreamApi implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return new BufferedReader(new InputStreamReader(inputStream))
                    .lines().collect(Collectors.joining("\n"));
        }
    }

    static class ParallelStreamApi implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return new BufferedReader(new InputStreamReader(inputStream)).lines()
                    .parallel().collect(Collectors.joining("\n"));
        }
    }

    static class ByteArrayOutputStreamTest implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            try(ByteArrayOutputStream result = new ByteArrayOutputStream()) {
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) != -1) {
                    result.write(buffer, 0, length);
                }

                return result.toString(UTF_8);
            }
        }
    }

    static class BufferReaderTest implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            String newLine = System.getProperty("line.separator");
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder result = new StringBuilder(UTF_8);
            String line;
            boolean flag = false;
            while ((line = reader.readLine()) != null) {
                result.append(flag ? newLine : "").append(line);
                flag = true;
            }
            return result.toString();
        }
    }

    static class BufferedInputStreamVsByteArrayOutputStream implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            BufferedInputStream bis = new BufferedInputStream(inputStream);
            ByteArrayOutputStream buf = new ByteArrayOutputStream();
            int result = bis.read();
            while (result != -1) {
                buf.write((byte) result);
                result = bis.read();
            }

            return buf.toString(UTF_8);
        }
    }

    static class InputStreamAndStringBuilder implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            int ch;
            StringBuilder sb = new StringBuilder(UTF_8);
            while ((ch = inputStream.read()) != -1)
                sb.append((char) ch);
            return sb.toString();
        }
    }

    static class Java9ISTransferTo implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            inputStream.transferTo(bos);
            return bos.toString(UTF_8);
        }
    }

    static class Java9ISReadAllBytes implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return new String(inputStream.readAllBytes(), UTF_8);
        }
    }

}

나는 자바 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 (라이브러리 없음) 솔루션입니다.

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

다음은 표준 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)을 사용하여 토큰을 분리하므로 스트림의 전체 내용에 대해 하나의 토큰 만 제공합니다.

입력 스트림의 인코딩에 대해 구체적으로 설명 할 필요가있는 경우, 사용하는 캐릭터 세트 ( 「UTF-8」등)를 나타내는 2 번째의 인수를 Scanner 생성자에 제공 할 수 있습니다.

모자 팁은 제게 언급 한 기사를 한 번 지적한 Jacob, 에게 간다.

EDITED : Patrick 의 제안 덕분에 빈 입력 스트림을 처리 할 때 기능이 더욱 강력 해졌 습니다. 한 번 더 편집 : nixed try / catch, Patrick의 방법은 간결합니다.


어때?

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

이것은 내 순수 자바 및 안드로이드 솔루션, 잘 작동합니다 ...

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

이렇게하는 좋은 방법은 아파치 공유 IOUtils 를 사용하여 InputStreamStringWriter 로 복사하는 것이다.

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

또는 스트림과 작성자를 섞어 사용하지 않으려면 ByteArrayOutputStream 사용할 수 있습니다.


파일 하나를 고려해 볼 때 먼저 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();
}

자바 9 솔루션은 여기 완성을 위해 :

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

readAllBytes 는 현재 JDK 9 메인 코드베이스에 있으므로 릴리스에 표시 될 가능성이 있습니다. JDK 9 스냅 샷 빌드를 사용하여 지금 시도 할 수 있습니다.


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가 제안한 바리에이션을 제안 할 것입니다 (이는 StringBuffer를 내부적으로 사용하는 StringWriter 사용을 피합니다).

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

Kotlin 사용자는 단순히 다음을 수행합니다.

println(InputStreamReader(is).readText())

이므로

readText()

Kotlin 표준 라이브러리의 내장 확장 메소드입니다.


또 다른 하나는 모든 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);
}

에있는 유틸리티 메소드는 org.springframework.util.StreamUtils에서와 비슷 FileCopyUtils하지만 완료되면 스트림을 열어 둡니다.


스트림 리더를 사용하는 경우 끝에 스트림을 닫아야합니다.

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

이 때문에 좋네요 :

  • 손 안전 Charset.
  • 읽기 버퍼 크기를 제어합니다.
  • 빌더의 길이를 프로비저닝 할 수 있으며 정확히 작성할 수 없습니다.
  • 라이브러리 의존성이 없습니다.
  • 자바 7 이상입니다.

그게 뭐야?

public static String convertStreamToString(InputStream is) {
   if (is == null) return null;
   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));
   } catch (Throwable t) {}
   return sb.toString();
}

다음 은 새로운 Java API 8 기반 솔루션입니다.이 솔루션은 새로운 Stream 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")));
}

다음은 바이트 배열 버퍼를 사용하는 JDK 만 사용하는 방법입니다. 이것은 실제로 commons-io 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으로 String제 3 자 라이브러리를 사용하지 않고는. 사용 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();
}

이것은 아파치 구현을 원하지만 전체 라이브러리를 원하지 않는 사람들을위한 org.apache.commons.io.IOUtils 소스 코드 에서부터 나온 대답 이다.

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




inputstream