Làm cách nào để đọc / chuyển đổi InputStream thành Chuỗi trong Java?


4063

Nếu bạn có một java.io.InputStreamđối tượng, bạn nên xử lý đối tượng đó như thế nào và tạo ra một String?


Giả sử tôi có một InputStreamdữ liệu chứa dữ liệu văn bản và tôi muốn chuyển đổi nó thành a String, vì vậy, ví dụ tôi có thể ghi nó vào một tệp nhật ký.

Cách dễ nhất để lấy InputStreamvà chuyển đổi nó thành một là Stringgì?

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

36
Các câu trả lời cho câu hỏi này chỉ hoạt động nếu bạn muốn đọc đầy đủ nội dung của luồng (cho đến khi nó được đóng lại). Vì điều đó không phải lúc nào cũng được dự định (các yêu cầu http có kết nối duy trì sẽ không bị đóng), nên các phương thức này sẽ chặn (không cung cấp cho bạn nội dung).
F1sh

21
Bạn cần biết và chỉ định mã hóa ký tự cho luồng hoặc bạn sẽ gặp lỗi mã hóa ký tự, vì bạn sẽ sử dụng mã hóa được chọn ngẫu nhiên tùy thuộc vào máy / hệ điều hành / nền tảng hoặc phiên bản mà mã của bạn được chạy. Đó là, không sử dụng các phương thức phụ thuộc vào mã hóa mặc định của nền tảng.
Christoffer Hammarström

11
Chỉ để vui vẻ với nhận xét của riêng tôi từ 9 năm trước, những ngày này tôi sử dụng "Chuỗi s = new File (" someFile.txt ") của Groov để đọc toàn bộ tệp cùng một lúc và nó hoạt động rất tốt. Tôi hài lòng với việc sử dụng Groovy cho mã không sản xuất (kịch bản) của mình và - thành thật buộc bạn phải xử lý mã hóa và các tệp cực kỳ dài theo cách java thực hiện là một ý tưởng thực sự tốt cho mã sản xuất vì vậy nó hoạt động cho mục đích của nó, Groovy hoạt động cho các tập lệnh nhanh mà java không giỏi - Chỉ cần sử dụng đúng công cụ cho công việc và tất cả đều hoạt động.
Bill K

Chỉ cần đơn giản hóa: ByteArrayOutputStream outputBytes = new ByteArrayOutputStream(); for(byte[] b = new byte[512]; 0 < inputStream.read(b); outputBytes.write(b)); return new String(outputBytes.toByteArray(), StandardCharsets.UTF_8);
Felypp Oliveira

@BillK với Java 11, bạn có thể sử dụng String s = Files.readString​(Path.of("SomeFile.txt"));ngôn ngữ tốt như ngôn ngữ có thể nhận được, điều này sẽ không bao giờ hỗ trợ các chuyển đổi loại ma thuật như ngôn ngữ bạn mô tả.
Holger

Câu trả lời:


2530

Một cách hay để làm điều này là sử dụng Apache commons IOUtils để sao chép InputStreamvào một StringWriter... thứ gì đó như

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

hoặc thậm chí

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

Ngoài ra, bạn có thể sử dụng ByteArrayOutputStreamnếu bạn không muốn kết hợp Luồng và Nhà văn của mình


75
Đối với các nhà phát triển Android, có vẻ như Android không đi kèm với IOUtils từ Apache. Vì vậy, bạn có thể xem xét tham khảo các câu trả lời khác.
Chris.Zou

47
Đây là một câu hỏi cực kỳ cũ tại thời điểm này (nó đã được hỏi vào năm 2008). Đó là giá trị thời gian của bạn để đọc qua các câu trả lời hiện đại hơn. Một số sử dụng các cuộc gọi riêng từ thư viện Java 8.
Shadoninja

36
Câu trả lời này đã lỗi thời và người ta có thể đánh dấu nó như vậy (đáng buồn là điều này không thể xảy ra).
codepleb

7
IOUtils.toString () đã bị từ chối. Câu trả lời này chắc chắn không phải là cách được đề nghị nữa.
Roshan

7
sau đó chỉnh sửa nó để giải thích lý do tại sao nó không được dùng để giúp người đọc trong tương lai.
Jean-François Fabre

2487

Tóm tắt các câu trả lời khác tôi tìm thấy 11 cách chính để làm điều này (xem bên dưới). Và tôi đã viết một số bài kiểm tra hiệu suất (xem kết quả bên dưới):

Các cách để chuyển đổi InputStream thành Chuỗi:

  1. Sử dụng IOUtils.toString( Sử dụng Apache)

    String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
  2. Sử dụng CharStreams(ổi)

    String result = CharStreams.toString(new InputStreamReader(
          inputStream, Charsets.UTF_8));
  3. Sử dụng Scanner(JDK)

    Scanner s = new Scanner(inputStream).useDelimiter("\\A");
    String result = s.hasNext() ? s.next() : "";
  4. Sử dụng API luồng (Java 8). Cảnh báo : Giải pháp này chuyển đổi các ngắt dòng khác nhau (như \r\n) thành \n.

    String result = new BufferedReader(new InputStreamReader(inputStream))
      .lines().collect(Collectors.joining("\n"));
  5. Sử dụng API Stream song song (Java 8). Cảnh báo : Giải pháp này chuyển đổi các ngắt dòng khác nhau (như \r\n) thành \n.

    String result = new BufferedReader(new InputStreamReader(inputStream)).lines()
       .parallel().collect(Collectors.joining("\n"));
  6. Sử dụng InputStreamReaderStringBuilder(JDK)

    final int bufferSize = 1024;
    final char[] buffer = new char[bufferSize];
    final StringBuilder out = new StringBuilder();
    Reader in = new InputStreamReader(stream, StandardCharsets.UTF_8);
    int charsRead;
    while((charsRead = in.read(buffer, 0, buffer.length)) > 0) {
        out.append(buffer, 0, charsRead);
    }
    return out.toString();
  7. Sử dụng StringWriterIOUtils.copy(Apache Commons)

    StringWriter writer = new StringWriter();
    IOUtils.copy(inputStream, writer, "UTF-8");
    return writer.toString();
  8. Sử dụng ByteArrayOutputStreaminputStream.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. Sử dụng BufferedReader(JDK). Cảnh báo: Giải pháp này chuyển đổi các ngắt dòng khác nhau (như \n\r) thành thuộc tính line.separatorhệ thống (ví dụ: trong Windows thành "\ 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. Sử dụng BufferedInputStreamByteArrayOutputStream(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. Sử dụng inputStream.read()StringBuilder(JDK). Cảnh báo : Giải pháp này có vấn đề với Unicode, ví dụ với văn bản tiếng Nga (chỉ hoạt động chính xác với văn bản không phải là Unicode)

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

Cảnh báo :

  1. Giải pháp 4, 5 và 9 chuyển đổi các ngắt dòng khác nhau thành một.

  2. Giải pháp 11 không thể hoạt động chính xác với văn bản Unicode

Kiểm tra hiệu năng

Các bài kiểm tra hiệu suất cho nhỏ String(chiều dài = 175), url trong github (chế độ = Thời gian trung bình, hệ thống = Linux, điểm số 1.343 là tốt nhất):

              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

Các bài kiểm tra hiệu năng cho big String(length = 50100), url trong github (mode = Average Time, system = Linux, điểm 200.715 là tốt nhất):

               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

Đồ thị (kiểm tra hiệu suất tùy thuộc vào độ dài Luồng đầu vào trong hệ thống Windows 7)
nhập mô tả hình ảnh ở đây

Kiểm tra hiệu suất (Thời gian trung bình) tùy thuộc vào độ dài Luồng đầu vào trong hệ thống 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

17
Khi bạn đang viết "câu trả lời tóm tắt", bạn nên lưu ý rằng một số giải pháp tự động chuyển đổi các ngắt dòng khác nhau (như \r\n) thành \ncó thể không mong muốn trong một số trường hợp. Ngoài ra, thật tuyệt khi thấy bộ nhớ bổ sung cần thiết hoặc ít nhất là áp lực phân bổ (ít nhất là bạn có thể chạy JMH với -prof gc). Đối với bài đăng thực sự thú vị, sẽ rất tuyệt khi xem biểu đồ (tùy thuộc vào độ dài chuỗi trong cùng kích thước đầu vào và tùy thuộc vào kích thước đầu vào trong cùng độ dài chuỗi).
Tagir Valeev 17/2/2016

16
Nâng cao; điều thú vị nhất là kết quả được mong đợi nhiều hơn: người ta nên sử dụng đường cú pháp tiêu chuẩn JDK và / hoặc Apache Commons.
Aleksei Matiushkin

25
Bài viết tuyệt vời. Một điều nữa thôi. Java 8 cảnh báo không sử dụng các luồng song song trên các tài nguyên sẽ buộc bạn phải khóa và chờ đợi (chẳng hạn như luồng đầu vào này) để tùy chọn luồng song song khá cồng kềnh và không đáng?
mangusbrother

10
Liệu luồng song song thực sự duy trì thứ tự dòng?
Natix

6
Là gì reset()cho trong ví dụ 11?
Rob Stewart

2307

Đây là một cách chỉ sử dụng thư viện Java tiêu chuẩn (lưu ý rằng luồng không bị đóng, số dặm của bạn có thể thay đổi).

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

Tôi đã học được mẹo này từ bài viết "Thủ thuật quét ngu ngốc" . Lý do nó hoạt động là vì Máy quét lặp lại các mã thông báo trong luồng và trong trường hợp này, chúng tôi tách các mã thông báo bằng cách sử dụng "bắt đầu ranh giới đầu vào" (\ A), do đó chỉ cung cấp cho chúng tôi một mã thông báo cho toàn bộ nội dung của luồng.

Lưu ý, nếu bạn cần cụ thể về mã hóa của luồng đầu vào, bạn có thể cung cấp đối số thứ hai cho hàm Scannertạo cho biết ký tự nào được sử dụng (ví dụ: "UTF-8").

Hat tip cũng đi đến Jacob , người đã từng chỉ cho tôi bài báo nói.


8
Cảm ơn, vì phiên bản này của tôi, tôi đã thêm một khối cuối cùng đóng luồng đầu vào, vì vậy người dùng không phải đọc vì bạn đã đọc xong đầu vào. Đơn giản hóa mã người gọi đáng kể.

4
@PavelRepin @Patrick trong trường hợp của tôi, một inputStream trống đã gây ra một NPE trong quá trình xây dựng Máy quét. Tôi đã phải thêm if (is == null) return "";ngay khi bắt đầu phương thức; Tôi tin rằng câu trả lời này cần được cập nhật để xử lý tốt hơn null inputStreams.
CFL_Jeff

115
Đối với Java 7, bạn có thể đóng lại trong một lần thử: try(java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; }
Earcam

5
Thật không may, giải pháp này dường như đi và mất các ngoại lệ được ném trong triển khai luồng cơ bản của tôi.
Taig

11
FYI, hasNext khối trên luồng đầu vào của bàn điều khiển (xem tại đây ). (Chỉ cần chạy vào vấn đề này ngay bây giờ.) Giải pháp này hoạt động tốt nếu không ... chỉ cần một cái đầu lên.
Ryan

848

Apache Commons cho phép:

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

Tất nhiên, bạn có thể chọn mã hóa ký tự khác ngoài UTF-8.

Cũng xem: ( tài liệu )


1
Ngoài ra, có một phương thức chỉ lấy một đối số inputStream, nếu bạn được tìm thấy với mã hóa mặc định.
Guillaume Coté

13
@Guillaume Coté Tôi đoán thông báo ở đây là bạn không bao giờ nên "ổn với mã hóa mặc định", vì bạn không thể chắc chắn về nó là gì, tùy thuộc vào nền tảng mà mã java được chạy.
Mỗi Wiklander

7
@Per Wiklander Tôi không đồng ý với bạn. Mã sẽ hoạt động trên một đơn có thể khá chắc chắn rằng mã hóa mặc định sẽ ổn. Đối với mã chỉ mở tệp cục bộ, đó là một tùy chọn hợp lý để yêu cầu chúng được mã hóa trong mã hóa mặc định của nền tảng.
Guillaume Coté

39
Để cứu bất kỳ ai gặp rắc rối với Google - <lệ thuộc> <groupId> org.apache.commons </ groupId> <artifactId> commons-io </ artifactId> <version> 1.3.2 </ version> </ Depencies>
Chris

7
Ngoài ra, một chút cải tiến sẽ là sử dụng hằng số apache io (hoặc loại khác) để mã hóa ký tự thay vì sử dụng chuỗi ký tự đơn giản - ví dụ: IOUtils.toString (myInputStream, Charsets.UTF_8);

300

Đưa vào tài khoản tập tin đầu tiên nên có một java.io.Readerví dụ. Điều này sau đó có thể được đọc và thêm vào một StringBuilder(chúng tôi không cần StringBuffernếu chúng tôi không truy cập nó trong nhiều luồng và StringBuildernhanh hơn). Mẹo ở đây là chúng ta làm việc theo khối và vì thế không cần các luồng đệm khác. Kích thước khối được tham số hóa để tối ưu hóa hiệu suất thời gian chạy.

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

8
Giải pháp này sử dụng các ký tự đa bào. Ví dụ sử dụng mã hóa UTF-8 cho phép biểu thị phạm vi unicode đầy đủ (Bao gồm cả tiếng Trung Quốc). Thay thế "UTF-8" bằng mã hóa khác sẽ cho phép mã hóa được sử dụng.
Paul de Vrieze

27
@ User1 - Tôi thích sử dụng các thư viện trong mã của mình để tôi có thể hoàn thành công việc của mình nhanh hơn. Thật tuyệt vời khi những người quản lý của bạn nói "Wow James! Làm thế nào mà bạn hoàn thành nó nhanh như vậy?!". Nhưng khi chúng ta phải dành thời gian để phát minh lại bánh xe chỉ vì chúng ta đã đặt nhầm ý tưởng về việc bao gồm một tiện ích chung, có thể tái sử dụng, đã thử nghiệm, chúng ta sẽ bỏ thời gian để có thể tiếp tục thực hiện các mục tiêu của dự án. Khi chúng tôi phát minh lại bánh xe, chúng tôi làm việc chăm chỉ gấp đôi nhưng vẫn về đích nhiều hơn sau đó. Một khi chúng tôi ở vạch đích, không có ai ở đó để chúc mừng chúng tôi. Khi xây nhà, đừng xây búa nữa
jmort253

10
Xin lỗi, sau khi đọc lại bình luận của tôi, nó tỏ ra hơi kiêu ngạo. Tôi chỉ nghĩ rằng điều quan trọng là phải có lý do chính đáng để tránh các thư viện và lý do đó là hợp lệ, điều đó rất có thể xảy ra :)
jmort253

4
@ jmort253 Chúng tôi nhận thấy hồi quy hiệu suất sau khi cập nhật một số thư viện trong sản phẩm của chúng tôi nhiều lần. May mắn thay, chúng tôi đang xây dựng và bán sản phẩm của chính mình để chúng tôi không thực sự có cái gọi là thời hạn. Thật không may, chúng tôi đang xây dựng một sản phẩm có sẵn trên nhiều JVM, cơ sở dữ liệu và máy chủ ứng dụng trên nhiều hệ thống hoạt động nên chúng tôi phải suy nghĩ cho người dùng sử dụng các máy kém ... Và tối ưu hóa hoạt động chuỗi có thể cải thiện độ hoàn hảo 30 ~ 40%. Và một sửa chữa: In our product, I even replacednên là 'chúng tôi thậm chí thay thế'.
coolcfan

10
@ jmort253 Nếu bạn đã sử dụng apache commons tôi sẽ nói, hãy dùng nó. Đồng thời, có một chi phí thực sự cho việc sử dụng các thư viện (vì sự phổ biến phụ thuộc trong nhiều thư viện java apache cho thấy). Nếu đây là lần sử dụng duy nhất của thư viện, thì việc sử dụng thư viện sẽ là quá mức cần thiết. Mặt khác, xác định (các) kích thước bộ đệm của riêng bạn, bạn có thể điều chỉnh cân bằng sử dụng bộ nhớ / bộ xử lý của mình.
Paul de Vrieze

248

Sử dụng:

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

11
Vấn đề là, trước tiên bạn chia thành các dòng, và sau đó hoàn tác điều đó. Nó dễ dàng hơn và nhanh hơn để chỉ đọc bộ đệm tùy ý.
Paul de Vrieze

20
Ngoài ra, readLine không phân biệt giữa \ n và \ r, do đó bạn không thể tái tạo luồng chính xác một lần nữa.
María Arias de Reyna Domínguez

2
rất không hiệu quả, như readLineđọc từng ký tự để tìm EOL. Ngoài ra, nếu không có ngắt dòng trong luồng, điều này không thực sự có ý nghĩa.
njzk2

3
@Gops AB: Nếu bạn dùng thử và mẫu của bạn có dòng mới trong đó, bạn sẽ thấy rằng cách vòng lặp này được xây dựng bằng cách sử dụng readline () và StringBuilder.append () trên thực tế không bảo toàn các dòng mới.
Russ Bateman

4
Đây không phải là câu trả lời tốt nhất vì nó không hoàn toàn theo byte. Người đọc nhai các dòng mới, vì vậy bạn phải cẩn thận để duy trì chúng.
Jeffrey Blattman

173

Nếu bạn đang sử dụng Google-Bộ sưu tập / Quả ổi, bạn có thể làm như sau:

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

Lưu ý rằng tham số thứ hai (ví dụ Charsets.UTF_8) InputStreamReaderkhông cần thiết, nhưng nói chung nên xác định mã hóa nếu bạn biết (bạn nên biết điều đó!)


2
@harschware: Đưa ra câu hỏi là: "Nếu bạn có đối tượng java.io.InputStream, bạn nên xử lý đối tượng đó như thế nào và tạo ra Chuỗi?" Tôi giả định rằng một luồng đã có mặt trong tình huống.
Sakuraba

Bạn đã không giải thích câu trả lời của bạn rất tốt, và có các biến không liên quan; user359996 nói điều tương tự như bạn, nhưng rõ ràng hơn nhiều.
Chữ viết tắt

2
+1 cho ổi, -1 vì không chỉ định mã hóa của luồng đầu vào. ví dụ. InputStreamReader mới (luồng, "UTF-8")
andras

@Chris Noldus Mặt khác, một số người đã có ổi trong dự án của họ, như tôi, và nghĩ rằng giải pháp này thanh lịch hơn phiên bản chỉ có sdk.
CorayThan

@Vadzim câu trả lời đó giống như câu trả lời này - cả hai đều sử dụng CharStreams.toString
Tom

125

Đây là giải pháp Java thuần túy tốt nhất phù hợp hoàn hảo cho Android và bất kỳ JVM nào khác.

Giải pháp này hoạt động rất tốt ... nó đơn giản, nhanh chóng và hoạt động trên các luồng nhỏ và lớn giống nhau !! (xem điểm chuẩn ở trên .. số 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;
}

4
Hoạt động tốt trên Android so với các câu trả lời khác chỉ hoạt động trong java doanh nghiệp.
xoáy

Sự cố trong Android với lỗi OutOfMemory trên dòng ".write", mỗi lần, cho các chuỗi ngắn.
Adam

Tôi đã thêm mã hóa. giống như một ghi chú bên cạnh, phương thức readFully ban đầu tôi có trong mã của mình không trả về Chuỗi, nó trả về byte [] cho chức năng mục đích chung hơn. Việc triển khai Chuỗi mới (...) bằng mã hóa là trách nhiệm của việc sử dụng API!
TacB0sS

2
Lưu ý nhanh: Dấu chân bộ nhớ này được tối đa hóa 2*n, trong đó n là kích thước của luồng, theo ByteArrayInputStreamhệ thống phát triển tự động.
njzk2

3
Tăng gấp đôi mức sử dụng bộ nhớ một cách không cần thiết, đó là điều quý giá trên các thiết bị di động. Bạn nên sử dụng InputStreamReader và nối vào StringReader, chuyển đổi byte sang char sẽ được thực hiện nhanh chóng, không phải hàng loạt vào cuối.
Oliv

84

Để hoàn thiện ở đây là giải pháp Java 9 :

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

Hiện readAllBytestại trong cơ sở mã chính của JDK 9, vì vậy nó có khả năng xuất hiện trong bản phát hành. Bạn có thể thử nó ngay bây giờ bằng cách sử dụng các bản dựng ảnh chụp nhanh JDK 9 .


Không phải phương pháp phân bổ toàn bộ bộ nhớ để đọc sao? byte[] buf = new byte[DEFAULT_BUFFER_SIZE];nơi MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;mà cho MAX_BUFFER_SIZE = 2147483639. Google cho biết khoảng 2.147 GB.
Rekin

Xin lỗi, tôi đã làm một lỗi trong tính toán. Nó là 2 GB. Tôi đã chỉnh sửa nhận xét. Vì vậy, ngay cả khi tôi đọc như một tệp 4kb, tôi sử dụng bộ nhớ 2gb?
Rekin

2
@ChristianHujer, tôi không thấy nó trong cam kết jdk8u mới nhất . Các phương thức mới của AFAIK không bao giờ được giới thiệu trong các bản cập nhật Java, chỉ trong các bản phát hành chính.
Tagir Valeev

4
@ChristianHujer, câu hỏi là về InputStream, không phải về Path. Có InputStreamthể được tạo từ nhiều nguồn khác nhau, không chỉ các tệp.
Tagir Valeev 2/2/2016

5
Điều này đã được viết cách đây một năm, vì vậy để cập nhật, tôi xác nhận rằng phương thức này thực sự là trong bản phát hành công khai JDK 9. Hơn nữa, nếu mã hóa của bạn là "ISO-Latin-1" thì điều này sẽ cực kỳ hiệu quả vì Chuỗi Java 9 hiện đang sử dụng một byte[]thực hiện nếu tất cả các nhân vật trong 256 điểm mã đầu tiên. Điều này có nghĩa là Chuỗi mới (byte [], "ISO-Latin-1") sẽ là một bản sao mảng đơn giản.
Klitos Kyriacou

66

Sử dụng:

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

@ DanielDeLeón Không, không. Đó là một BufferedInputStream. Các lần đọc cơ bản là 8192 byte mỗi lần.
Hầu tước Lorne

2
@EJP Tôi thấy nó chậm hơn so với việc sử dụng BufferedInputStream đọc vào bộ đệm mảng byte thay vì một byte mỗi lần. Ví dụ: 200ms so với 60ms khi đọc tệp MiB 4,56.
jk7 17/03/2017

Điều lạ là không ai chỉ ra vấn đề lớn khác ở đây (vâng, đọc từng byte nội dung là lãng phí ngay cả với bộ đệm): nó phụ thuộc vào bất cứ điều gì xảy ra là "mã hóa mặc định" - đây hiếm khi là một cách tốt. Thay vào đó, hãy đảm bảo chuyển mã hóa làm đối số buf.toString().
StaxMan

@ jk7 Thời gian để đọc tệp 4,56 MB rất nhỏ đến mức sự khác biệt có thể có thể có ý nghĩa.
Hầu tước Lorne

63

Đây là giải pháp Java thuần túy (không có thư viện) thanh lịch nhất mà tôi đã nghĩ ra sau một số thử nghiệm:

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

8
@TorbenKohlmeier, độc giả và bộ đệm không cần phải đóng cửa. Việc cung cấp InputStreamphải được đóng lại bởi người gọi.
vẽ Noakes

7
Đừng quên đề cập rằng có một hàm tạo thích hợp hơn trong InputStreamReader có Charset.
jontejj

7
Tại sao mọi người cứ sử dụng readLine? nếu bạn không sử dụng các dòng trên mỗi se, nó có ích gì (ngoại trừ việc rất chậm?)
njzk2

4
Đừng đọc theo dòng. Điều gì sẽ xảy ra nếu một dòng quá dài để nó không phù hợp với đống?
voho

4
@voho, nếu một dòng dài như vậy, thì không có cách nào để phân bổ giá trị trả về dù sao phải có kích thước bằng hoặc lớn hơn cho dòng đó. Nếu bạn đang xử lý các tệp lớn, bạn nên truyền phát chúng. Có rất nhiều trường hợp sử dụng để tải các tệp văn bản nhỏ vào bộ nhớ.
Drew Noakes

55

Tôi đã làm một điểm chuẩn trên 14 câu trả lời khác nhau ở đây (xin lỗi vì không cung cấp tín dụng nhưng có quá nhiều bản sao).

Kết quả rất đáng ngạc nhiên. Hóa ra Apache IOUtils là chậm nhất và ByteArrayOutputStreamlà giải pháp nhanh nhất:

Vì vậy, đầu tiên ở đây là phương pháp tốt nhất:

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

Kết quả điểm chuẩn, 20 byte ngẫu nhiên trong 20 chu kỳ

Thời gian tính bằng mili giây

  • ByteArrayOutputStreamTest: 194
  • NioStream: 198
  • Java9ISTransferTo: 201
  • Java9ISRead ALLBytes: 205
  • BufferedInputStreamVsByteArrayOutputStream: 314
  • ApacheStringWriter2: 574
  • Quả ổiCharStreams: 589
  • Máy quétReaderNextTest: 614
  • Máy quét: 633
  • ApacheStringWriter: 1544
  • StreamApi: Lỗi
  • ParallelStreamApi: Lỗi
  • BufferReaderTest: Lỗi
  • InputStreamAndStringBuilder: Lỗi

Mã nguồn chuẩn

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

}

Tạo điểm chuẩn trong Java không dễ dàng (đặc biệt là vì JIT). Sau khi đọc mã nguồn Điểm chuẩn, tôi tin rằng những giá trị trên không chính xác và mọi người nên cẩn thận bằng cách tin vào chúng.
Dalibor

@Dalibor có lẽ bạn nên cung cấp nhiều lý do hơn cho yêu cầu của mình thay vì chỉ là một liên kết.
Ilya Gazman

Tôi nghĩ rằng thực tế đã biết rằng thực sự không dễ để tạo ra điểm chuẩn của riêng bạn. Đối với những người không biết điều đó, có liên kết;)
Dalibor

@Dalibor Có lẽ tôi không phải là người giỏi nhất, nhưng tôi hiểu rất rõ về các tiêu chuẩn Java, vì vậy trừ khi bạn có thể chỉ ra một vấn đề cụ thể, bạn chỉ hiểu sai và tôi sẽ không tiếp tục cuộc trò chuyện với bạn trong những điều kiện đó.
Ilya Gazman

Chủ yếu là tôi đồng ý với Dalibor. Bạn nói rằng bạn có "sự hiểu biết tốt về các điểm chuẩn Java", nhưng dường như bạn đã thực hiện cách tiếp cận ngây thơ nhất trong khi dường như không biết gì về các vấn đề nổi tiếng của phương pháp này. Để bắt đầu, hãy đọc mọi bài đăng về câu hỏi này: stackoverflow.com/questions/504103/ trên
DavidS

41

Tôi sẽ sử dụng một số thủ thuật 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.
    }
}

Về cơ bản giống như một số câu trả lời khác ngoại trừ cô đọng hơn.


5
Điều đó có return nullbao giờ được gọi? Hoặc là br.lines...trả lại hoặc một ngoại lệ được ném.
Holloway

3
@Khaled Một Khunaifer: vâng, khá chắc chắn ... có lẽ bạn nên xem ở đây: docs.oracle.com/javase/tutorial/essential/exceptions/ trên . Những gì bạn chỉnh sửa sai là một tuyên bố "thử tài nguyên".
jamp 5/2/2015

11
Tại sao bạn gọi parallel()trên luồng?
cướp

4
Điều này sẽ không dẫn đến một bản sao trung thực của dữ liệu nếu luồng nguồn sử dụng kết thúc dòng cửa sổ vì tất cả \r\ncuối cùng sẽ được chuyển đổi thành \n...
Lucas

2
Bạn có thể sử dụng System.lineSeparator()để sử dụng kết thúc dòng phụ thuộc nền tảng thích hợp.
Steve K

34

Tôi đã chạy một số bài kiểm tra thời gian bởi vì vấn đề thời gian, luôn luôn.

Tôi đã cố gắng để có được phản hồi vào Chuỗi 3 cách khác nhau. (hiển thị bên dưới)
Tôi bỏ qua các khối thử / bắt vì tính dễ đọc.

Để đưa ra bối cảnh, đây là mã trước cho cả 3 cách tiếp cận:

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

Vì vậy, sau khi chạy 500 thử nghiệm trên mỗi phương pháp với cùng một dữ liệu yêu cầu / phản hồi, đây là những con số. Một lần nữa, đây là những phát hiện của tôi và những phát hiện của bạn có thể không hoàn toàn giống nhau, nhưng tôi đã viết điều này để đưa ra một số dấu hiệu cho những người khác về sự khác biệt hiệu quả của các phương pháp này.

Xếp hạng:
Cách tiếp cận số 1
Cách tiếp cận số 3 - chậm hơn 2,6% so với
cách tiếp cận số 1 - chậm hơn 4,3% so với cách tiếp cận số 1

Bất kỳ cách tiếp cận nào trong số này là một giải pháp thích hợp để lấy phản hồi và tạo Chuỗi từ nó.


2
2) có lỗi, nó luôn luôn thêm "null" ở cuối chuỗi vì bạn luôn thực hiện thêm một bước sau đó là cần thiết. Hiệu suất sẽ giống nhau dù sao tôi nghĩ. Điều này sẽ hoạt động: Chuỗi read = null; StringBuffer sb = new StringBuffer (); while (đọc = br.readLine ())! = null) {sb.append (đọc); }
LukeSolar

Cần lưu ý rằng GetMethod là một phần của org.apache.commons.httpclient, không phải Java tiêu chuẩn
jk7

Cách tiếp cận số 2 sẽ tiêu thụ '\ n' nếu tệp có nhiều dòng, đây không phải là câu trả lời
Ninja

33

Giải pháp Java thuần túy sử dụng Stream s, hoạt động kể từ Java 8.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

// ...
public static String inputStreamToString(InputStream is) throws IOException {
    try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
        return br.lines().collect(Collectors.joining(System.lineSeparator()));
    }
}

Như được đề cập bởi Christoffer Hammarström bên dưới câu trả lời khác , sẽ an toàn hơn khi chỉ định rõ ràng Bộ ký tự . Tức là hàm tạo InputStreamReader có thể thay đổi như sau:

new InputStreamReader(is, Charset.forName("UTF-8"))

11
Thay vì làm Charset.forName("UTF-8"), sử dụng StandardCharsets.UTF_8(từ java.nio.charset).
cướp

26

Đây là câu trả lời ít nhiều của sampath, làm sạch một chút và được biểu diễn dưới dạng hàm:

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


21

Nếu bạn không thể sử dụng Commons IO (FileUtils / IOUtils / CopyUtils), thì đây là một ví dụ sử dụng BufferedReader để đọc từng dòng tệp:

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

Hoặc nếu bạn muốn tốc độ thô, tôi sẽ đề xuất một biến thể về những gì Paul de Vrieze đã đề xuất (tránh sử dụng StringWriter (sử dụng StringBuffer bên trong):

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

Để làm cho mã của bạn hoạt động, tôi đã phải sử dụng this.getClass (). GetClassLoader (). GetResourceAsStream () (sử dụng Eclipse với một dự án maven)
greuze

19

Điều này là tốt đẹp bởi vì:

  • Nó xử lý Charset một cách an toàn.
  • Bạn kiểm soát kích thước bộ đệm đọc.
  • Bạn có thể cung cấp độ dài của trình tạo và nó không phải là một giá trị chính xác.
  • Được miễn phí từ phụ thuộc thư viện.
  • Dành cho Java 7 trở lên.

Làm thế nào để làm nó?

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

Dành cho JDK 9

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

1
Không catch (Throwable)nên thực sự trống nếu đây là mã sản xuất.
Christian Hujer

1
Những gì để đưa vào bắt này - tuyên bố ném?
alex

Mặc dù sử dụng UTF-8 thường hợp lý, bạn không nên cho rằng các ký tự được mã hóa theo cách đó.
Martin

18

Đây là một câu trả lời được điều chỉnh từ org.apache.commons.io.IOUtils mã nguồn , dành cho những người muốn thực hiện apache nhưng không muốn toàn bộ thư viện.

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

18

Đảm bảo đóng luồng cuối nếu bạn sử dụng Trình đọc luồng

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

EDIT: Trên JDK 7+, bạn có thể sử dụng cấu trúc try-with-resource.

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

2
Tuy nhiên, bạn đúng về việc đóng luồng, trách nhiệm đóng luồng thường là với trình tạo luồng (hoàn thành những gì bạn bắt đầu). Vì vậy, iStreamthực sự nên được đóng bởi người gọi vì người gọi đã tạo iStream. Ngoài ra, việc đóng các luồng nên được thực hiện trong một finallykhối hoặc thậm chí tốt hơn trong câu lệnh thử tài nguyên Java 7. Trong mã của bạn, khi readLine()ném IOExceptionhoặc builder.append()ném OutOfMemoryError, các luồng sẽ vẫn mở.
Christian Hujer

16

Một số khác, cho tất cả người dùng 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);
}

Các phương thức tiện ích org.springframework.util.StreamUtilstương tự như các phương thức trong FileCopyUtils, nhưng chúng để luồng mở khi hoàn thành.


16

Sử dụng java.io.InputStream.transferTo (OutputStream) được hỗ trợ trong Java 9 và ByteArrayOutputStream.toString (Chuỗi) có tên bộ ký tự:

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

Những gì bạn đã thông qua cho tên bộ ký tự trong trường hợp của bạn?
virsha

1
@virsha Bạn phải xác định rằng từ nguồn đã cung cấp InputStream. Hãy nhớ rằng không có nghĩa là có một chuỗi mà không biết nó sử dụng mã hóa nào.
jmehren

15

Đây là phương pháp hoàn chỉnh để chuyển đổi InputStreamthành Stringmà không cần sử dụng bất kỳ thư viện bên thứ ba nào. Sử dụng StringBuildercho môi trường đơn luồng nếu không sử dụng 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();
}

3
Trong phương pháp này không có mã hóa được áp dụng. Vì vậy, giả sử dữ liệu nhận được từ InputStream được mã hóa bằng UTF-8, đầu ra sẽ sai. Để khắc phục điều này bạn có thể sử dụng in = new InputStreamReader(inputStream)(char)in.read().
Frederic Leitenberger

2
và bộ nhớ không hiệu quả là tốt; Tôi tin rằng tôi đã thử sử dụng điều này trước đây trên một đầu vào lớn và StringBuilder đã hết bộ nhớ
gengkev

1
Có một câu trả lời tương tự khác sử dụng bộ đệm char [] và hiệu quả hơn và chăm sóc bộ ký tự.
Guillaume Perrot

14

Đây là cách thực hiện bằng cách chỉ sử dụng JDK bằng cách sử dụng bộ đệm mảng byte. Đây thực sự là cách các IOUtils.copy()phương thức commons-io hoạt động. Bạn có thể thay thế byte[]bằng char[]nếu bạn đang sao chép từ một Readerthay vì InputStream.

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

1
Vui lòng đưa ra một mô tả về những gì bạn đang cố gắng để đạt được.
Ragunath Jawahar

14

Người dùng Kotlin chỉ cần làm:

println(InputStreamReader(is).readText())

trong khi

readText()

là phương pháp mở rộng tích hợp của thư viện tiêu chuẩn Kotlin.


Điều này thực sự không hoàn toàn chính xác vì nó không đóng luồng. Tôi muốn giới thiệu is.bufferedReader().use { it.readText() }.
Tối đa

9

Cách dễ nhất trong JDK là với các đoạn mã sau.

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

7

Đây là giải pháp dựa trên Java 8 của tôi , sử dụng API Stream mới để thu thập tất cả các dòng từ một InputStream:

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

1
Có vẻ như bạn không thực sự đọc tất cả các câu trả lời được đăng trước đó. Phiên bản API Stream đã ở đây ít nhất hai lần .
Tagir Valeev

Tôi đã xem xét tất cả các giải pháp, nhưng thấy không phù hợp. Tôi tìm thấy hai dòng với một mô tả ngắn được trình bày chính xác. Ví dụ, khối thử bắt từ giải pháp khác không bao giờ được sử dụng. Nhưng bạn nói đúng. Với rất nhiều câu trả lời, tôi chuyển sang chế độ đọc nhanh ... :-)
Christian Rädel

1
Bạn không đọc tệp gốc, bạn đang chuyển đổi bất kỳ dòng kết thúc nào tệp có thành bất kỳ dòng kết thúc nào mà HĐH có, có thể thay đổi nội dung tệp.
Christian Hujer

7

Về mặt reduceconcatnó có thể được biểu thị trong Java 8 như:

String fromFile = new BufferedReader(new   
InputStreamReader(inputStream)).lines().reduce(String::concat).get();

1
Nó sẽ rất chậm.
Tagir Valeev

Thú vị, tại sao? Bạn có thể giải thích?
libnull-dev

1
bạn không biết tại sao nối chuỗi trong vòng lặp thay vì sử dụng StringBuilder là một ý tưởng tồi?
Tagir Valeev

Bạn đúng rồi. StringBuildercó thể hiệu quả hơn Tôi sẽ kiểm tra, nhưng quan điểm của tôi là chỉ ra cách tiếp cận nhiều chức năng hơn với sự bất biến String.
libnull-dev

Phương pháp tiếp cận chức năng là mát mẻ nhưng thường rất không hiệu quả.
Lluis Martinez

4

Câu trả lời JDK 7/8 đóng luồng và vẫn ném 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));
  }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.