ฉันจะอ่าน / แปลง InputStream เป็น String ใน Java ได้อย่างไร


4063

หากคุณมีjava.io.InputStreamวัตถุคุณควรประมวลผลวัตถุนั้นและสร้างStringอย่างไร


สมมติว่าฉันInputStreamมีข้อความที่มีข้อมูลตัวอักษรและฉันต้องการแปลงเป็น a Stringดังนั้นตัวอย่างเช่นฉันสามารถเขียนมันลงในไฟล์บันทึกได้

วิธีที่ง่ายที่สุดในการใช้InputStreamและแปลงเป็น a Stringคืออะไร?

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

36
คำตอบสำหรับคำถามนี้ใช้ได้เฉพาะเมื่อคุณต้องการอ่านเนื้อหาของสตรีมอย่างเต็มที่ (จนกว่าจะปิด) เนื่องจากไม่ได้มีเจตนาเสมอไป (คำขอ HTTP ที่มีการเชื่อมต่อแบบ keep-alive จะไม่ถูกปิด) วิธีการเหล่านี้จึงเรียกบล็อก (ไม่ให้เนื้อหากับคุณ)
f1sh

21
คุณจำเป็นต้องรู้และระบุการเข้ารหัสอักขระสำหรับสตรีมหรือคุณจะมีข้อบกพร่องในการเข้ารหัสอักขระเนื่องจากคุณจะใช้การเข้ารหัสแบบสุ่มที่เลือกขึ้นอยู่กับเครื่อง / ระบบปฏิบัติการ / แพลตฟอร์มหรือเวอร์ชั่นที่โค้ดของคุณทำงานอยู่ นั่นคือจะไม่ใช้วิธีการที่ขึ้นอยู่กับการเข้ารหัสแพลตฟอร์มเริ่มต้น
Christoffer Hammarström

11
เพียงเพื่อสนุกกับความคิดเห็นของตัวเองเมื่อ 9 ปีที่แล้ววันนี้ฉันใช้ Groovy "String s = new File (" SomeFile.txt "). text" เพื่ออ่านไฟล์ทั้งหมดในครั้งเดียวและใช้งานได้ดี ฉันมีความสุขกับการใช้ groovy สำหรับโค้ดที่ไม่ใช่การผลิต (สคริปต์) ของฉันและ - บังคับให้คุณจัดการกับการเข้ารหัสและไฟล์ที่ยาวมากอย่างที่สุจริตในแบบที่ java ใช้เป็นความคิดที่ดีจริงๆสำหรับโค้ดการผลิตต่อไป Groovy ใช้ได้กับสคริปต์ที่รวดเร็วซึ่งจาวาไม่ได้ยอดเยี่ยมเพียงใช้เครื่องมือที่เหมาะสมกับงาน
Bill K

ทำให้ง่ายขึ้น: 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 กับ Java 11 คุณสามารถใช้งานString s = Files.readString​(Path.of("SomeFile.txt"));ได้ดีเท่าที่ภาษาจะได้รับซึ่งจะไม่สนับสนุนการแปลงประเภทเวทย์มนตร์เช่นที่คุณอธิบายไว้
Holger

คำตอบ:


2530

วิธีที่ดีในการทำเช่นนี้คือใช้Apache คอมมอนส์ IOUtilsเพื่อคัดลอก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); 

หรือคุณสามารถใช้ByteArrayOutputStreamหากคุณไม่ต้องการผสมสตรีมและนักเขียนของคุณ


75
สำหรับนักพัฒนา android ดูเหมือนว่า android ไม่ได้มาพร้อมกับ IOUtils จาก Apache ดังนั้นคุณอาจลองอ้างถึงคำตอบอื่น ๆ
Chris.Zou

47
นี่เป็นคำถามที่เก่ามากอย่างไม่น่าเชื่อในตอนนี้ (มันถูกถามในปี 2008) มันคุ้มค่ากับเวลาที่จะอ่านคำตอบที่ทันสมัยกว่านี้ บางคนใช้การโทรแบบเนทีฟจากไลบรารี Java 8
Shadoninja

36
คำตอบนี้ล้าสมัยอย่างมากและควรทำเครื่องหมายว่าเป็นเช่นนี้ (น่าเศร้าที่เป็นไปไม่ได้ที่ ATM)
codepleb

7
IOUtils.toString () เลิกใช้มานานแล้ว คำตอบนี้แน่นอนไม่ใช่วิธีที่แนะนำอีกต่อไป
Roshan

7
จากนั้นแก้ไขเพื่ออธิบายว่าทำไมเลิกใช้เพื่อช่วยผู้อ่านในอนาคต
Jean-François Fabre

2486

สรุปคำตอบอื่น ๆ ที่ฉันพบ 11 วิธีหลักในการทำเช่นนี้ (ดูด้านล่าง) และฉันเขียนการทดสอบประสิทธิภาพ (ดูผลลัพธ์ด้านล่าง):

วิธีในการแปลง InputStream เป็น String:

  1. การใช้IOUtils.toString(Apache Utils)

    String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
  2. การใช้CharStreams(ฝรั่ง)

    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. ใช้Stream API (Java 8) คำเตือน : การแก้ปัญหานี้จะแปลงแบ่งบรรทัดที่แตกต่างกัน (ชอบ\r\n) \nไป

    String result = new BufferedReader(new InputStreamReader(inputStream))
      .lines().collect(Collectors.joining("\n"));
  5. การใช้Stream Stream 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(stream, StandardCharsets.UTF_8);
    int charsRead;
    while((charsRead = in.read(buffer, 0, buffer.length)) > 0) {
        out.append(buffer, 0, charsRead);
    }
    return out.toString();
  7. การใช้StringWriterและIOUtils.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 แปลงตัวแบ่งบรรทัดที่แตกต่างกันเป็นหนึ่ง

  2. โซลูชันที่ 11 ไม่สามารถทำงานอย่างถูกต้องด้วยข้อความ Unicode

การทดสอบประสิทธิภาพ

การทดสอบประสิทธิภาพสำหรับขนาดเล็กString(ความยาว = 175), URL เป็นGithub (โหมด = เวลาเฉลี่ย, ระบบ = 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), urit in github (โหมด = เวลาเฉลี่ย, ระบบ = 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

17
ในขณะที่คุณกำลังเขียน "คำตอบสรุป" คุณควรทราบว่าโซลูชันบางอย่างจะแปลง linebreaks ที่ต่างกัน (เช่น\r\n) \nซึ่งอาจไม่เป็นที่ต้องการในบางกรณีโดยอัตโนมัติ นอกจากนี้ยังเป็นการดีที่จะเห็นหน่วยความจำเพิ่มเติมที่ต้องการหรืออย่างน้อยก็ต้องมีความกดดันในการจัดสรร (อย่างน้อยคุณอาจเรียกใช้ JMH ด้วย-prof gc) สำหรับโพสต์ที่ยอดเยี่ยมจริงๆมันจะเป็นการดีที่ได้เห็นกราฟ (ขึ้นอยู่กับความยาวสตริงภายในขนาดอินพุตเดียวกันและขึ้นอยู่กับขนาดอินพุตภายในความยาวสตริงเดียวกัน)
Tagir Valeev

16
upvoted; สิ่งที่สนุกที่สุดคือผลลัพธ์ที่ได้เกินคาด: ควรใช้ JDK มาตรฐานและ / หรือ Apache Commons น้ำตาล
Aleksei Matiushkin

25
โพสต์ที่น่าตื่นตาตื่นใจ เพียงสิ่งหนึ่ง. Java 8 เตือนการใช้กระแสข้อมูลแบบขนานบนทรัพยากรที่จะบังคับให้คุณล็อคและรอ (เช่นอินพุตสตรีม) ดังนั้นตัวเลือกสตรีมแบบขนานค่อนข้างยุ่งยากและไม่คุ้มค่าหรือไม่?
mangusbrother

10
สตรีมแบบขนานจริง ๆ จะรักษาการเรียงลำดับบรรทัดหรือไม่?
Natix

6
อะไรคือสิ่งที่reset()สำหรับในตัวอย่าง 11?
Rob Stewart

2307

นี่คือวิธีการใช้เฉพาะไลบรารี Java มาตรฐาน (โปรดทราบว่าสตรีมไม่ได้ถูกปิดระยะของคุณอาจแตกต่างกันไป)

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

ผมได้เรียนรู้เคล็ดลับนี้จาก"เทคนิคสแกนเนอร์โง่"บทความ เหตุผลก็คือเครื่องสแกนเนอร์ทำซ้ำโทเค็นในสตรีมและในกรณีนี้เราแยกโทเค็นโดยใช้ "การเริ่มต้นของขอบเขตอินพุต" (\ A) ดังนั้นจึงให้เราโทเค็นเดียวสำหรับเนื้อหาทั้งหมดของสตรีม

หมายเหตุหากคุณต้องการระบุเฉพาะเกี่ยวกับการเข้ารหัสของอินพุตสตรีมคุณสามารถระบุอาร์กิวเมนต์ที่สองให้กับตัวScannerสร้างที่ระบุชุดอักขระที่จะใช้ (เช่น "UTF-8")

ปลายหมวกยังไปถึงยาโคบซึ่งครั้งหนึ่งฉันเคยชี้ไปที่บทความดังกล่าว


8
ขอบคุณสำหรับเวอร์ชันนี้ของฉันฉันเพิ่มบล็อกสุดท้ายที่ปิดอินพุตสตรีมดังนั้นผู้ใช้ไม่จำเป็นต้องทำตั้งแต่คุณอ่านอินพุตเสร็จแล้ว ลดความซับซ้อนของรหัสผู้โทร

4
@PavelRepin @Patrick ในกรณีของฉัน inputStream ที่ว่างเปล่าทำให้เกิด NPE ในระหว่างการสร้างสแกนเนอร์ ฉันต้องเพิ่มif (is == null) return "";ที่จุดเริ่มต้นของวิธีการ; ฉันเชื่อว่าคำตอบนี้ต้องได้รับการปรับปรุงเพื่อจัดการ null inputStreams ให้ดียิ่งขึ้น
CFL_Jeff

115
สำหรับ Java 7 คุณสามารถปิดการทดลองใช้: try(java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; }
earcam

5
น่าเสียดายที่โซลูชันนี้ดูเหมือนว่าจะดำเนินต่อไปและสูญเสียข้อยกเว้นที่เกิดขึ้นในการใช้งานสตรีมพื้นฐาน
Taig

11
FYI, มีบล็อกถัดไปในสตรีมอินพุตคอนโซล (ดูที่นี่ ) (เพิ่งพบปัญหานี้ในตอนนี้) โซลูชันนี้ทำงานได้ดีอย่างอื่น ... แค่หัวขึ้น
Ryan

848

Apache Commons อนุญาต:

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

แน่นอนคุณสามารถเลือกการเข้ารหัสอักขระอื่นนอกเหนือจาก UTF-8

ดูเพิ่มเติมที่: ( เอกสารประกอบ )


1
นอกจากนี้ยังมีวิธีที่ใช้อาร์กิวเมนต์ inputStream เท่านั้นหากคุณพบว่ามีการเข้ารหัสเริ่มต้น
Guillaume Coté

13
@ Guillaume Cotéฉันเดาว่าข้อความที่นี่คือคุณไม่ควร "ดีกับการเข้ารหัสเริ่มต้น" เนื่องจากคุณไม่แน่ใจว่ามันคืออะไรขึ้นอยู่กับแพลตฟอร์มที่โค้ดจาวาทำงานอยู่
ต่อ Wiklander

7
@Per Wiklander ฉันไม่เห็นด้วยกับคุณ รหัสที่จะใช้งานในเครื่องเดียวอาจจะค่อนข้างแน่ใจว่าการเข้ารหัสเริ่มต้นจะใช้ได้ สำหรับรหัสที่เปิดเฉพาะไฟล์ในตัวเครื่องเป็นตัวเลือกที่เหมาะสมในการขอให้เข้ารหัสในการเข้ารหัสเริ่มต้นของแพลตฟอร์ม
Guillaume Coté

39
เพื่อช่วยให้ทุกคนไม่ต้องกังวลกับ Googling - <dependency> <groupId> org.apache.commons </groupId> <artifactId> commons-io </artifactId> <version> 1.3.2 </version> </dependency>
Chris

7
การปรับปรุงเล็ก ๆ น้อย ๆ ก็คือการใช้ค่าคงที่ apache io (หรืออื่น ๆ ) สำหรับการเข้ารหัสอักขระแทนการใช้สตริงตัวอักษรธรรมดา - เช่น: IOUtils.toString (myInputStream, Charsets.UTF_8);

300

คำนึงถึงไฟล์บัญชีหนึ่งควรได้รับjava.io.Readerตัวอย่าง สิ่งนี้สามารถอ่านและเพิ่มไปยัง a 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();
}

8
วิธีนี้ใช้อักขระหลายไบต์ ตัวอย่างใช้การเข้ารหัส UTF-8 ที่ช่วยให้การแสดงออกของช่วง Unicode เต็ม (รวมถึงภาษาจีน) การแทนที่ "UTF-8" ด้วยการเข้ารหัสอื่นจะช่วยให้สามารถใช้การเข้ารหัสได้
Paul de Vrieze

27
@ User1 - ฉันชอบใช้ห้องสมุดในรหัสของฉันเพื่อให้สามารถทำงานได้เร็วขึ้น มันยอดเยี่ยมมากเมื่อผู้จัดการของคุณพูดว่า "Wow James! แต่เมื่อเราต้องใช้เวลาในการปรับแต่งวงล้อเพียงเพราะเรามีความคิดที่ผิดเกี่ยวกับการรวมยูทิลิตี้ทั่วไปที่ใช้ซ้ำได้ลองและทดสอบเราจะให้เวลาเราจะใช้จ่ายต่อไปเป้าหมายของโครงการของเรา เมื่อเราบูรณาการล้อเราทำงานหนักเป็นสองเท่า แต่ไปถึงเส้นชัยในภายหลัง เมื่อเรามาถึงเส้นชัยแล้วก็ไม่มีใครแสดงความยินดีกับเรา เมื่อสร้างบ้านอย่าสร้างค้อนด้วย
jmort253

10
ขออภัยหลังจากอ่านความคิดเห็นของฉันอีกครั้งมันก็ออกมาหยิ่งเล็กน้อย ฉันแค่คิดว่ามันเป็นสิ่งสำคัญที่จะมีเหตุผลที่ดีที่จะหลีกเลี่ยงห้องสมุดและเหตุผลก็คือเหตุผลที่ถูกต้องซึ่งอาจเป็นไปได้ที่ดีมาก :)
jmort253

4
@ jmort253 เราสังเกตเห็นการถดถอยของประสิทธิภาพหลังจากอัปเดตไลบรารีบางรายการในผลิตภัณฑ์ของเราหลายครั้ง โชคดีที่เรากำลังสร้างและขายผลิตภัณฑ์ของเราเองดังนั้นเราจึงไม่มีกำหนดเวลาที่เรียกว่าจริง ๆ น่าเสียดายที่เรากำลังสร้างผลิตภัณฑ์ที่มีอยู่ใน JVM หลายฐานข้อมูลและเซิร์ฟเวอร์แอปในระบบปฏิบัติการหลายระบบดังนั้นเราต้องคิดว่าผู้ใช้ที่ใช้เครื่องจักรที่ไม่ดี ... และการเพิ่มประสิทธิภาพการทำงานของสตริงสามารถปรับปรุง perf ได้ 30 ~ 40% และการแก้ไข: In our product, I even replacedควรเป็น 'เรายังถูกแทนที่ด้วย'
coolcfan

10
@ jmort253 หากคุณจะใช้ apache ทั่วไปแล้วฉันจะบอกว่าไปเลย ในเวลาเดียวกันมีค่าใช้จ่ายจริงในการใช้ห้องสมุด (ตามการเพิ่มจำนวนการพึ่งพาในไลบรารี apache java จำนวนมากแสดง) หากนี่เป็นเพียงการใช้งานไลบรารีเท่านั้นมันจะเกินความสามารถในการใช้งานไลบรารี ในอีกทางหนึ่งการกำหนดขนาดบัฟเฟอร์ของคุณเองคุณสามารถปรับแต่งหน่วยความจำการใช้หน่วยความจำ / หน่วยประมวลผลของคุณ
Paul de Vrieze

248

ใช้:

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
สิ่งนี้คือคุณแยกเป็นเส้นแรกแล้วเลิกทำสิ่งนั้น ง่ายกว่าและเร็วกว่าในการอ่านบัฟเฟอร์โดยพลการ
Paul de Vrieze

20
นอกจากนี้ readLine จะไม่แยกความแตกต่างระหว่าง \ n และ \ r ดังนั้นคุณจึงไม่สามารถสร้างกระแสข้อมูลที่แน่นอนอีกครั้ง
María Arias de Reyna Domínguez

2
ไม่มีประสิทธิภาพมากเป็นreadLineตัวละครอ่านโดยตัวละครที่จะมองหา EOL นอกจากนี้หากไม่มีการแบ่งบรรทัดในสตรีมสิ่งนี้จะไม่สมเหตุสมผล
njzk2

3
@Gops AB: หากคุณลองใช้งานและตัวอย่างของคุณมีการขึ้นบรรทัดใหม่คุณจะเห็นว่าวิธีการสร้างวงนี้โดยใช้ readline () และ StringBuilder.append () ไม่ได้รักษาบรรทัดใหม่ไว้
Russ Bateman

4
นี่ไม่ใช่คำตอบที่ดีที่สุดเพราะมันไม่ได้ไบต์อย่างเคร่งครัด ผู้อ่าน chomps ขึ้นบรรทัดใหม่ดังนั้นคุณต้องระมัดระวังในการรักษาไว้
Jeffrey Blattman

173

หากคุณใช้ Google-Collections / Guava คุณสามารถทำสิ่งต่อไปนี้:

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

โปรดทราบว่าพารามิเตอร์ที่สอง (เช่น Charsets.UTF_8) สำหรับInputStreamReaderไม่จำเป็น แต่โดยทั่วไปควรระบุการเข้ารหัสหากคุณรู้ (ซึ่งคุณควร!)


2
@harschware: ตั้งคำถามว่า: "ถ้าคุณมีวัตถุ java.io.InputStream คุณควรประมวลผลวัตถุนั้นและผลิตสตริงได้อย่างไร" ฉันสันนิษฐานว่ากระแสมีอยู่แล้วในสถานการณ์
Sakuraba

คุณไม่ได้อธิบายคำตอบของคุณเป็นอย่างดีและมีตัวแปรที่ไม่เกี่ยวข้อง user359996 พูดอย่างเดียวกับคุณ แต่ชัดเจนกว่า
Uronym

2
+1 สำหรับฝรั่ง, -1 เพื่อไม่ระบุการเข้ารหัสของอินพุตสตรีม เช่น. ใหม่ InputStreamReader (สตรีม, "UTF-8")
andras

@Chris Noldus ในทางกลับกันบางคนมีฝรั่งในโครงการของพวกเขาเช่นฉันแล้วและคิดว่าวิธีนี้ดีกว่ารุ่น sdk เท่านั้น
CorayThan

@Vadzim คำตอบนั้นเหมือนกับคนนี้ - ทั้งคู่ใช้ CharStreams.toString
Tom

125

นี่เป็นวิธีแก้ปัญหา Java ที่ดีที่สุดที่เหมาะกับ Android และ JVM อื่น ๆ

วิธีนี้ใช้ได้ดีอย่างน่าอัศจรรย์ ... มันง่ายรวดเร็วและทำงานบนสตรีมขนาดเล็กและใหญ่เหมือนกัน !! (ดูมาตรฐานด้านบน .. หมายเลข 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
ทำงานได้ดีบน Android เมื่อเทียบกับคำตอบอื่น ๆ ซึ่งทำงานได้เฉพาะใน enterprise java
vortexwolf

ขัดข้องใน Android ที่มีข้อผิดพลาด OutOfMemory ในบรรทัด ".write" ทุกครั้งสำหรับสตริงสั้น ๆ
อดัม

ฉันได้เพิ่มการเข้ารหัส เช่นเดียวกับการบันทึกด้านข้างวิธี readFully ต้นฉบับที่ฉันมีในโค้ดของฉันไม่ส่งคืน String มันจะส่งกลับไบต์ [] เพื่อการใช้งานทั่วไปที่มากขึ้น การใช้ String ใหม่ (... ) พร้อมการเข้ารหัสเป็นความรับผิดชอบของ on ที่ใช้ API!
TacB0sS

2
บันทึกย่อด่วน: รอยเท้าหน่วยความจำของสิ่งนี้ถูกขยายให้ใหญ่สุดโดย2*nที่ n คือขนาดของสตรีมตามByteArrayInputStreamระบบการเติบโตอัตโนมัติ
njzk2

3
การใช้งานหน่วยความจำสองเท่าโดยไม่จำเป็นซึ่งมีค่าสำหรับอุปกรณ์มือถือ คุณควรที่จะใช้ InputStreamReader และผนวกเข้าไปที่ StringReader การแปลงไบต์เป็นถ่านจะเสร็จสิ้นทันทีไม่ใช่จำนวนมากในตอนท้าย
Oliv

84

เพื่อความสมบูรณ์นี่คือโซลูชันJava 9 :

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

readAllBytesขณะนี้อยู่ใน JDK 9 codebase หลักดังนั้นจึงมีแนวโน้มที่จะปรากฏในการปล่อย คุณสามารถลองตอนนี้ใช้JDK 9 ภาพรวมสร้าง


วิธีการจัดสรรหน่วยความจำจำนวนมากไม่ให้อ่านหรือไม่ byte[] buf = new byte[DEFAULT_BUFFER_SIZE];ที่ซึ่งจะช่วยให้MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; MAX_BUFFER_SIZE = 2147483639Google กล่าวว่ามีขนาดประมาณ 2.147 GB
Rekin

ขออภัยฉันมีข้อผิดพลาดในการคำนวณ มันคือ 2 GB ฉันแก้ไขความคิดเห็นแล้ว ดังนั้นแม้ว่าฉันจะอ่านไฟล์ 4kb ฉันก็ใช้หน่วยความจำ 2gb?
Rekin

2
@ChristianHujer ผมไม่เห็นมันในล่าสุด jdk8u กระทำ วิธีการใหม่ ๆ ของ AFAIK นั้นไม่เคยนำมาใช้ในการอัพเดท Java เฉพาะในรุ่นใหญ่เท่านั้น
Tagir Valeev

4
@ChristianHujer คำถามเป็นเรื่องที่ไม่เกี่ยวกับInputStream สามารถสร้างขึ้นจากแหล่งข้อมูลที่แตกต่างกันหลายไฟล์ที่ไม่เพียง แต่ PathInputStream
Tagir Valeev

5
สิ่งนี้เขียนเมื่อปีที่แล้วดังนั้นเพื่ออัปเดตฉันขอยืนยันว่าวิธีนี้เป็นจริงในรุ่นสาธารณะ JDK 9 นอกจากนี้หากการเข้ารหัสของคุณคือ "ISO-Latin-1" ดังนั้นสิ่งนี้จะมีประสิทธิภาพอย่างยิ่งตั้งแต่ Java 9 Strings การbyte[]ใช้งานหากตัวละครทั้งหมดอยู่ในจุดรหัส 256 คนแรก ซึ่งหมายความว่า String ใหม่ (ไบต์ [], "ISO-Latin-1") จะเป็นอาเรย์สำเนาอย่างง่าย
Klitos Kyriacou

66

ใช้:

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ไม่มันไม่ได้ BufferedInputStreamมันเป็น การอ่านที่สำคัญคือ 8192 ไบต์ในแต่ละครั้ง
user207421

2
@EJP ฉันพบว่ามันช้ากว่าการใช้BufferedInputStream และการอ่านลงในบัฟเฟอร์อาร์เรย์ไบต์แทนหนึ่งไบต์ในเวลา ตัวอย่าง: 200ms vs 60ms เมื่ออ่านไฟล์ 4.56 MiB
jk7

แปลกที่ไม่มีใครชี้ให้เห็นปัญหาสำคัญอื่น ๆ ที่นี่ (ใช่การอ่านเนื้อหาไบต์ต่อไบต์จะสิ้นเปลืองแม้จะมีบัฟเฟอร์): มันขึ้นอยู่กับสิ่งที่เกิดขึ้นเป็น "การเข้ารหัสเริ่มต้น" - นี่เป็นวิธีที่ดี buf.toString()แต่ให้แน่ใจว่าจะผ่านการเข้ารหัสเป็นอาร์กิวเมนต์ไป
StaxMan

@ jk7 เวลาในการอ่านไฟล์ 4.56MB นั้นเล็กมากจนความแตกต่างนั้นไม่อาจมีนัยสำคัญได้
user207421

63

ต่อไปนี้เป็นโซลูชันที่หรูหราและบริสุทธิ์ที่สุดของ 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();
}

8
@TorbenKohlmeier ผู้อ่านและบัฟเฟอร์ไม่จำเป็นต้องปิด ที่ให้InputStreamควรจะปิดโดยผู
Drew Noakes

7
อย่าลืมพูดถึงว่ามี Constructor ที่นิยมกว่าใน InputStreamReader ที่ใช้ CharSet
jontejj

7
ทำไมคนให้ใช้readLine? ถ้าคุณไม่ใช้เส้นต่อ se อะไรดี (ยกเว้นช้ามาก?)
njzk2

4
อย่าอ่านตามบรรทัด เกิดอะไรขึ้นถ้าหนึ่งบรรทัดยาวมากดังนั้นมันจึงไม่พอดีกับกอง
voho

4
@voho หากหนึ่งบรรทัดนั้นยาวมากแล้วก็ไม่มีทางที่จะจัดสรรค่าส่งคืนได้ซึ่งต้องมีขนาดเท่ากันหรือมากกว่านั้นในบรรทัดนั้น หากคุณกำลังจัดการกับไฟล์ที่มีขนาดใหญ่คุณควรทำการสตรีม มีกรณีการใช้งานมากมายสำหรับการโหลดไฟล์ข้อความขนาดเล็กลงในหน่วยความจำ
Drew Noakes

55

ฉันทำเกณฑ์มาตรฐานตามคำตอบที่แตกต่างกัน 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 MB สุ่มไบต์ในรอบ 20

เวลาเป็นมิลลิวินาที

  • ByteArrayOutputStreamTest: 194
  • NioStream: 198
  • Java9ISTransferTo: 201
  • Java9ISReadAllBytes: 205
  • BufferedInputStreamVsByteArrayOutputStream: 314
  • ApacheStringWriter2: 574
  • GuavaCharStreams: 589
  • ScannerReaderNoNextTest: 614
  • สแกนเนอร์ผู้อ่าน: 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);
        }
    }

}

การสร้างมาตรฐานใน Java ไม่ใช่เรื่องง่าย (โดยเฉพาะอย่างยิ่งเนื่องจาก JIT) หลังจากอ่านซอร์สโค้ดเกณฑ์มาตรฐานฉันเชื่อว่าค่าข้างต้นนั้นไม่แม่นยำและทุกคนควรระมัดระวังโดยเชื่อพวกเขา
Dalibor

@Dalibor คุณอาจให้เหตุผลเพิ่มเติมสำหรับการเรียกร้องของคุณมากกว่าเพียงแค่ลิงก์
Ilya Gazman

ฉันคิดว่ามันเป็นความจริงที่รู้กันดีว่ามันไม่ง่ายเลยที่จะสร้างมาตรฐานของคุณเอง สำหรับผู้ที่ไม่ทราบว่ามีการเชื่อมโยง;)
Dalibor

@Dalibor ฉันอาจไม่ใช่คนที่ดีที่สุด แต่ฉันมีความเข้าใจที่ดีเกี่ยวกับเกณฑ์มาตรฐานของ Java ดังนั้นหากคุณไม่สามารถชี้ให้เห็นปัญหาที่เฉพาะเจาะจงคุณกำลังทำให้เข้าใจผิดและฉันจะไม่พูดคุยกับคุณต่อไปภายใต้เงื่อนไขเหล่านั้น
Ilya Gazman

ส่วนใหญ่ฉันเห็นด้วยกับ Dalibor คุณบอกว่าคุณมี "ความเข้าใจที่ดีเกี่ยวกับการวัดประสิทธิภาพของ Java" แต่ดูเหมือนว่าคุณได้ใช้วิธีการที่ไร้เดียงสามากที่สุดในขณะที่ยังไม่รู้ประเด็นที่เป็นที่รู้จักกันดีของวิธีการนี้ สำหรับผู้เริ่มอ่านโพสต์ทุกคำถามนี้: stackoverflow.com/questions/504103/…
DavidS

41

ฉันจะใช้เทคนิค 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.
    }
}

เป็นหลักเหมือนกับคำตอบอื่น ๆ ยกเว้นรวบรัดมากขึ้น


5
มันจะreturn nullถูกเรียกไหม? ทั้งbr.lines...ผลตอบแทนหรือยกเว้นจะโยน
Holloway

3
@Khaled Khunaifer: ใช่สวยว่า ... บางทีคุณอาจจะได้ดูที่นี่: docs.oracle.com/javase/tutorial/essential/exceptions/... สิ่งที่คุณแก้ไขอย่างไม่ถูกต้องคือคำสั่ง "ลองกับทรัพยากร"
jamp

11
เหตุใดคุณจึงโทรหาparallel()สตรีม
robinst

4
นี้จะไม่ส่งผลให้เกิดความซื่อสัตย์สำเนาของข้อมูลถ้ากระแสที่มาใช้ตอนจบหน้าต่างสายทั้งหมด\r\nจะจบลงด้วยการได้รับการแปลงเป็น\n...
ลูคัส

2
คุณสามารถใช้System.lineSeparator()เพื่อใช้การสิ้นสุดบรรทัดขึ้นอยู่กับแพลตฟอร์มที่เหมาะสม
Steve K

34

ฉันวิ่งทดสอบเวลาเพราะเวลาสำคัญเสมอ

ฉันพยายามรับการตอบสนองเป็น String 3 วิธีที่แตกต่างกัน (แสดงด้านล่าง)
ฉันออกไปลอง / จับบล็อกเพื่อประโยชน์ในการอ่าน

เพื่อให้บริบทนี่เป็นรหัสก่อนหน้าสำหรับทั้ง 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 ครั้งในแต่ละวิธีด้วยข้อมูลการร้องขอ / ตอบกลับที่เหมือนกันนี่คือตัวเลข อีกครั้งสิ่งเหล่านี้คือสิ่งที่ฉันค้นพบและสิ่งที่คุณค้นพบอาจไม่เหมือนกัน แต่ฉันเขียนสิ่งนี้เพื่อให้สิ่งบ่งชี้แก่คนอื่น ๆ เกี่ยวกับประสิทธิภาพที่แตกต่างของวิธีการเหล่านี้

อันดับ:
Approach # 1
Approach # 3 - 2.6% ช้ากว่า # 1
Approach # 2 - 4.3% ช้ากว่า # 1

วิธีการใด ๆ เหล่านี้เป็นโซลูชั่นที่เหมาะสมสำหรับการตอบสนองและการสร้างสตริงจากมัน


2
2) มีข้อผิดพลาดมันจะเพิ่ม "null" เสมอในตอนท้ายของสตริงในขณะที่คุณทำอีกหนึ่งขั้นตอนจำเป็น การแสดงจะเหมือนเดิมอยู่ดีฉันคิดว่า สิ่งนี้ควรใช้งานได้: String read = null; StringBuffer sb = new StringBuffer (); ในขณะที่ ((อ่าน = br.readLine ())! = null) {sb.append (อ่าน); }
LukeSolar

ควรสังเกตว่า GetMethod เป็นส่วนหนึ่งของ org.apache.commons.httpclient ไม่ใช่ Java มาตรฐาน
jk7

วิธีที่ # 2 จะใช้ '\ n' หากไฟล์มีหลายบรรทัดนี่ไม่ใช่คำตอบ
Ninja

33

โซลูชัน Pure Java ที่ใช้Stream s ทำงานได้ตั้งแต่ 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()));
    }
}

ดังกล่าวโดย Christoffer Hammarströmด้านล่างคำตอบอื่น ๆจะปลอดภัยอย่างชัดเจนระบุรหัสตัวอักษร เช่นคอนสตรัคเตอร์ InputStreamReader สามารถเปลี่ยนแปลงได้ดังต่อไปนี้:

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

11
แทนที่จะทำCharset.forName("UTF-8")ใช้StandardCharsets.UTF_8(จากjava.nio.charset)
robinst

26

ต่อไปนี้เป็นคำตอบของ 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();
}

24

หากคุณรู้สึกผจญภัยคุณสามารถผสม Scala และ Java และจบลงด้วยสิ่งนี้:

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

การผสมโค้ด Java และ Scala และไลบรารีมีประโยชน์

ดูคำอธิบายแบบเต็มได้ที่นี่: วิธีที่เป็นเอกลักษณ์ในการแปลง InputStream เป็น String ใน Scala


3
ทุกวันนี้มันใช้งานได้ดี:Source.fromInputStream(...).mkString
KajMagnus

21

หากคุณไม่สามารถใช้ 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);
    }
}

เพื่อที่จะทำให้การทำงานของรหัสของคุณผมใช้ this.getClass () getClassLoader () getResourceAsStream () (โดยใช้ Eclipse กับโครงการ Maven ก)..
Greuze

19

อันนี้ดีเพราะ:

  • มันจัดการกับ Charset อย่างปลอดภัย
  • คุณสามารถควบคุมขนาดบัฟเฟอร์การอ่าน
  • คุณสามารถกำหนดความยาวของผู้สร้างและไม่จำเป็นต้องเป็นค่าที่แน่นอน
  • เป็นอิสระจากการอ้างอิงห้องสมุด
  • ใช้สำหรับ 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);
    }
}

1
catch (Throwable)ไม่ควรจริงๆจะว่างเปล่าถ้าเป็นรหัสการผลิต
Christian Hujer

1
สิ่งที่จะใส่ในคำสั่ง catchable นี้
alex

ในขณะที่ใช้ UTF-8 โดยปกติแล้วคุณไม่ควรคิดว่าตัวอักษรนั้นถูกเข้ารหัส
Martin

18

นี่คือคำตอบที่ดัดแปลงมาจากorg.apache.commons.io.IOUtils ซอร์สโค้ดสำหรับผู้ที่ต้องการใช้งาน 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();
}

18

ตรวจสอบให้แน่ใจว่าได้ปิดกระแสในตอนท้ายหากคุณใช้ Stream Readers

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+ คุณสามารถใช้โครงสร้างลองกับทรัพยากร

/**
 * 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
คุณถูกต้องเกี่ยวกับการปิดสตรีมอย่างไรก็ตามความรับผิดชอบในการปิดสตรีมมักจะอยู่กับตัวสร้างสตรีม (เสร็จสิ้นสิ่งที่คุณเริ่มต้น) ดังนั้นควรจริงๆค่อนข้างถูกปิดโดยโทรเพราะโทรที่สร้างขึ้นiStream iStreamนอกจากนี้การปิดสตรีมควรกระทำในfinallyบล็อกหรือดีกว่าในคำสั่ง Java 7 ลองกับทรัพยากร ในรหัสของคุณเมื่อreadLine()พ่นIOExceptionหรือbuilder.append()ขว้างOutOfMemoryErrorกระแสจะยังคงเปิดอยู่
Christian Hujer

16

อีกหนึ่งสำหรับผู้ใช้สปริง:

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เปิด แต่จะเปิดสตรีมไว้เมื่อเสร็จสิ้น


16

ใช้java.io.InputStream.transferTo (OutputStream) ที่สนับสนุนใน Java 9 และByteArrayOutputStream.toString (String)ซึ่งใช้ชื่อ charset:

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

คุณส่งอะไรให้ชื่อชุดอักขระในกรณีของคุณ?
virsha

1
@virsha คุณต้องพิจารณาว่าจากแหล่งที่ให้ InputStream โปรดจำไว้ว่ามันไม่สมเหตุสมผลที่จะมีสตริงโดยไม่รู้ว่าใช้การเข้ารหัสอะไร
jmehrens

15

นี่คือวิธีการที่สมบูรณ์แบบสำหรับการแปลงInputStreamลงไปStringโดยไม่ต้องใช้ห้องสมุดของบุคคลที่สามใด ๆ ใช้สำหรับสภาพแวดล้อมเกลียวเดียวมิฉะนั้นการใช้งาน StringBuilderStringBuffer

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
ในวิธีนี้ไม่มีการเข้ารหัสที่ใช้ ดังนั้นสมมติว่าข้อมูลที่ได้รับจาก InputStream ถูกเข้ารหัสโดยใช้ UTF-8 เอาต์พุตจะผิด เพื่อแก้ไขปัญหานี้คุณสามารถใช้และin = new InputStreamReader(inputStream) (char)in.read()
Frederic Leitenberger

2
และหน่วยความจำไม่มีประสิทธิภาพเช่นกัน; ฉันเชื่อว่าฉันพยายามใช้นี้มาก่อนในการป้อนข้อมูลขนาดใหญ่และ StringBuilder วิ่งออกมาจากหน่วยความจำ
gengkev

1
มีคำตอบที่คล้ายกันอีกข้อหนึ่งซึ่งใช้บัฟเฟอร์ [] และมีประสิทธิภาพมากขึ้นและดูแลชุดอักขระ
Guillaume Perrot

14

ต่อไปนี้เป็นวิธีการใช้ JDK โดยใช้บัฟเฟอร์อาร์เรย์แบบไบต์ นี่เป็นวิธีการที่คอมมอนส์ - 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);

1
โปรดให้รายละเอียดเกี่ยวกับสิ่งที่คุณพยายามทำ
Ragunath Jawahar

14

ผู้ใช้ Kotlin ทำ:

println(InputStreamReader(is).readText())

แต่ทว่า

readText()

เป็นวิธีส่วนขยายในตัวของไลบรารีมาตรฐาน Kotlin


นี่ไม่ถูกต้องนักเพราะมันไม่ได้ปิดสตรีม is.bufferedReader().use { it.readText() }ผมอยากแนะนำให้
Max

9

วิธีที่ง่ายที่สุดใน JDK คือตัวอย่างโค้ดต่อไปนี้

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

7

นี่คือโซลูชันที่ใช้Java 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")));
}

1
ดูเหมือนว่าคุณไม่ได้อ่านคำตอบทั้งหมดที่โพสต์ก่อนหน้านี้ รุ่นสตรีม API ได้แล้วที่นี่อย่างน้อยสองครั้ง
Tagir Valeev

ฉันดูวิธีแก้ปัญหาทั้งหมด แต่ไม่พบวิธีที่เหมาะสม ฉันพบว่ามีสองบรรทัดที่มีคำอธิบายสั้น ๆ ถูกนำเสนออย่างแม่นยำ try-catch-block จากโซลูชันอื่นเป็นตัวอย่างที่ไม่เคยใช้ แต่คุณพูดถูก ด้วยคำตอบมากมายฉันเปลี่ยนเป็นโหมดอ่านข้ามอย่างรวดเร็ว ... :-)
Christian Rädel

1
คุณไม่ได้อ่านไฟล์ต้นฉบับคุณกำลังแปลงไฟล์ใด ๆ ที่ลงท้ายด้วยไฟล์ใด ๆ ก็ตามไปยังบรรทัดที่ลงท้ายด้วย OS ที่อาจเปลี่ยนแปลงเนื้อหาไฟล์
Christian Hujer

7

ในแง่ของreduceและconcatมันสามารถแสดงใน Java 8 เป็น:

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

1
มันจะช้าเมามัน
Tagir Valeev

น่าสนใจทำไม คุณสามารถทำอย่างละเอียด?
libnull-dev

1
คุณไม่ทราบหรือไม่ว่าทำไมการต่อสตริงเข้าด้วยกันแทนที่จะใช้ StringBuilder เป็นความคิดที่ไม่ดี?
Tagir Valeev

คุณพูดถูก StringBuilderอาจมีประสิทธิภาพมากขึ้น ฉันจะตรวจสอบ Stringแต่จุดของฉันคือการแสดงวิธีการทำงานมากขึ้นด้วยการเปลี่ยนรูป
libnull-dev

ฟังก์ชั่นการใช้งานนั้นเจ๋ง แต่มักจะไม่มีประสิทธิภาพมาก
Lluis Martinez

4

คำตอบ 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));
  }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.