เทียบเท่าฝรั่งสำหรับ IOUtils.toString (InputStream)


106

Apache Commons IOมีวิธีการอำนวยความสะดวกที่ดีIOUtils.toString ()ในการอ่านInputStreamสตริง

เนื่องจากฉันพยายามย้ายออกจาก Apache Commons และไปที่Guava : มี Guava เทียบเท่าหรือไม่? ฉันดูคลาสทั้งหมดในcom.google.common.ioแพ็กเกจแล้วก็ไม่พบอะไรง่ายๆเลย

แก้ไข:ฉันเข้าใจและขอบคุณปัญหาเกี่ยวกับชุดอักขระ มันเกิดขึ้นมากจนฉันรู้ว่าแหล่งที่มาทั้งหมดของฉันอยู่ใน ASCII (ใช่ ASCII ไม่ใช่ ANSI เป็นต้น) ดังนั้นในกรณีนี้การเข้ารหัสไม่ใช่ปัญหาสำหรับฉัน


2
เกี่ยวกับชุดอักขระ: ยังดีที่ห้องสมุดจะกำหนดให้คุณระบุว่าคุณรู้ว่าคุณกำลังจัดการกับชุดอักขระใด (เช่นCharsets.US_ASCII) แทนที่จะให้คุณพูดว่า "เอ๊ะมีชุดอักขระใดที่ฉันเดา" ซึ่งสำหรับหลาย ๆ คนดูเหมือนจะมีความสุข โดยเฉพาะอย่างยิ่งเนื่องจาก Java ไม่ใช้ค่าเริ่มต้นที่สมเหตุสมผลเช่น UTF-8
ColinD

ฉันรู้ว่า. นั่นเป็นเหตุผลที่ฉันใช้ UTF-8 เป็นเวอร์ชันเริ่มต้นในคำตอบของฉันเอง
Sean Patrick Floyd

ดูเอกสารเพิ่มเติม: code.google.com/p/guava-libraries/wiki/IOExplained
Vadzim

@Vadzim เอกสารเหล่านั้นไม่มีอยู่เมื่อถามคำถามนี้ :-)
Sean Patrick Floyd

คำตอบ:


85

คุณระบุในความคิดเห็นของคุณเกี่ยวกับคำตอบของ Calum ที่คุณกำลังจะใช้

CharStreams.toString(new InputStreamReader(supplier.get(), Charsets.UTF_8))

รหัสนี้มีปัญหาเนื่องจากCharStreams.toString(Readable)สถานะโอเวอร์โหลด:

ไม่ปิดไฟล์Readable.

ซึ่งหมายความว่าของคุณInputStreamReaderและโดยส่วนขยายที่InputStreamส่งคืนโดยsupplier.get()จะไม่ถูกปิดหลังจากโค้ดนี้เสร็จสมบูรณ์

ในทางกลับกันหากคุณใช้ประโยชน์จากข้อเท็จจริงที่ว่าคุณมีInputSupplier<InputStream>และใช้งานเกินพิกัดอยู่แล้วCharStreams.toString(InputSupplier<R extends Readable & Closeable>) toStringวิธีนี้จะจัดการทั้งการสร้างและการปิดReaderสำหรับคุณ

นี่คือสิ่งที่ Jon Skeet แนะนำยกเว้นว่าไม่มีการโอเวอร์โหลดใด ๆCharStreams.newReaderSupplierที่ใช้InputStreamเป็นอินพุต ... คุณต้องให้สิ่งนี้InputSupplier:

InputSupplier<? extends InputStream> supplier = ...
InputSupplier<InputStreamReader> readerSupplier = 
    CharStreams.newReaderSupplier(supplier, Charsets.UTF_8);

// InputStream and Reader are both created and closed in this single call
String text = CharStreams.toString(readerSupplier);

ประเด็นInputSupplierคือทำให้ชีวิตของคุณง่ายขึ้นโดยให้ Guava จัดการส่วนที่ต้องใช้try-finallyบล็อกที่น่าเกลียดเพื่อให้แน่ใจว่าทรัพยากรถูกปิดอย่างเหมาะสม

แก้ไข: โดยส่วนตัวแล้วฉันพบสิ่งต่อไปนี้ (ซึ่งเป็นวิธีที่ฉันเขียนจริงเพียงแค่แบ่งขั้นตอนในโค้ดด้านบน)

String text = CharStreams.toString(
    CharStreams.newReaderSupplier(supplier, Charsets.UTF_8));

ที่จะห่างไกล verbose น้อยกว่านี้:

String text;
InputStreamReader reader = new InputStreamReader(supplier.get(), 
    Charsets.UTF_8);
boolean threw = true;
try {
  text = CharStreams.toString(reader);
  threw = false;
}
finally {
  Closeables.close(reader, threw);
}

ซึ่งไม่มากก็น้อยสิ่งที่คุณต้องเขียนเพื่อจัดการกับสิ่งนี้อย่างเหมาะสมด้วยตัวคุณเอง


แก้ไข: กุมภาพันธ์ 2014

InputSupplierและOutputSupplierและวิธีการที่ใช้พวกเขาได้เลิกใช้แล้วใน Guava 16.0 ทดแทนของพวกเขาByteSource, CharSource, และByteSink CharSinkระบุByteSourceตอนนี้คุณสามารถรับเนื้อหาได้Stringดังนี้:

ByteSource source = ...
String text = source.asCharSource(Charsets.UTF_8).read();

ขอบคุณสำหรับข้อมูลดีๆ (+1) แต่นี่เป็นเรื่องที่ละเอียดมาก ฉันคิดว่าการรวมคำตอบที่ยอมรับกับ Closeables.closeQuietly () นั้นง่ายกว่า
Sean Patrick Floyd

@CollinD: ฉันใช้วิธีของคุณในคำตอบข้อหนึ่งของฉันโปรดดูโค้ดและบอกฉันว่านี่เป็นวิธีที่ถูกต้องในการใช้ InputSupplier หรือไม่
Emil

1
@ColinD ถ้า inputStream มาจากด้านในของ servlet doPost จะมีจุดใดในการปิดหรือไม่ (หรือกังวลเกี่ยวกับการปิด)
Blankman

CharStreams.toString (InputSupplier) เลิกใช้งานแล้ว ฉันสร้าง CharSource (จาก ByteSource โดยใช้ asCharSource) จากนั้นใช้ toString ตามที่เอกสารแนะนำ
John Lehmann

4
@ TedM.Young: ถ้าสิ่งที่คุณต้องเป็นInputStreamและคุณต้องการที่จะได้รับมันเป็นString, CharStreams.toString(new InputStreamReader(inputStream, charset))คือวิธีที่จะไป ByteSourceและCharSourceโดยเฉพาะสำหรับกรณีที่คุณมีบางสิ่งที่สามารถทำหน้าที่เป็นแหล่งที่มาของInputStreams หรือReaders
ColinD

56

หากคุณมีReadableคุณสามารถใช้CharStreams.toString(Readable). ดังนั้นคุณอาจทำสิ่งต่อไปนี้ได้:

String string = CharStreams.toString( new InputStreamReader( inputStream, "UTF-8" ) );

บังคับให้คุณระบุชุดอักขระซึ่งฉันเดาว่าคุณควรทำต่อไป


4
อันที่จริงฉันจะใช้คำตอบของคุณและ Jon Skeet ผสมกัน: `` CharStreams.toString (InputStreamReader ใหม่ (supplier.get (), Charsets.UTF_8)) `
Sean Patrick Floyd

ใช่มีหลายวิธีในการรวมตัวเลือก!
Calum

10
@SPFloyd: หากคุณมีInputSupplier<InputStream>ฉันขอแนะนำอย่างยิ่งให้ใช้CharStreams.newReaderSupplier(supplier, Charsets.UTF_8)มากกว่าnew InputStreamReader. เหตุผลก็คือเมื่อได้รับInputStreamReaderแล้วtoStringจะไม่ปิดสิ่งนั้นReader(และไม่ใช่สตรีมพื้นฐาน!) โดยใช้InputSupplierสำหรับReaderการtoStringวิธีการที่จะจัดการปิดReaderสำหรับคุณ
ColinD

17

UPDATE : มองย้อนกลับไปฉันไม่ชอบโซลูชันเก่าของฉัน นอกจากนี้ยังเป็นปี 2013 และตอนนี้มีทางเลือกอื่นที่ดีกว่าสำหรับ Java7 นี่คือสิ่งที่ฉันใช้ตอนนี้:

InputStream fis = ...;
String text;
try (  InputStreamReader reader = new InputStreamReader(fis, Charsets.UTF_8)){
        text = CharStreams.toString(reader);
}

หรือถ้ามีInputSupplier

InputSupplier<InputStreamReader> spl = ...
try (  InputStreamReader reader = spl.getInput()){
        text = CharStreams.toString(reader);
    }

16

เกือบ คุณสามารถใช้สิ่งนี้:

InputSupplier<InputStreamReader> readerSupplier = CharStreams.newReaderSupplier
    (streamSupplier, Charsets.UTF_8);
String text = CharStreams.toString(readerSupplier);

โดยส่วนตัวแล้วฉันไม่คิดว่าIOUtils.toString(InputStream)"ดี" - เพราะมันมักจะใช้การเข้ารหัสเริ่มต้นของแพลตฟอร์มซึ่งแทบจะไม่เป็นสิ่งที่คุณต้องการเลย มีการโอเวอร์โหลดซึ่งใช้ชื่อของการเข้ารหัส แต่การใช้ชื่อไม่ใช่ความคิดที่ดี IMO นั่นเป็นเหตุผลที่ฉันชอบCharsets.*นั่นเป็นเหตุผลที่ผมชอบ

แก้ไข: ไม่ใช่ว่าข้างต้นต้องการInputSupplier<InputStream>เป็นไฟล์streamSupplier. หากคุณมีสตรีมอยู่แล้วคุณสามารถใช้งานได้ง่ายพอ:

InputSupplier<InputStream> supplier = new InputSupplier<InputStream>() {
    @Override public InputStream getInput() {
        return stream;
    }
};

จอนสตรีมผ่าน request.getInputStream หรือไม่ นอกจากนี้คุณจะปิดสตรีมเหมือนที่ ColinD กล่าวไว้ในคำตอบของ @ Calum หรือไม่?
Blankman

โอ้และมันเป็นสภาวะแวดล้อม doPost แบบ servlet ฉันควรปิดสตรีมหรือไม่?
Blankman

@ แบล็คแมน: อ่านั่นคือบริบทของคุณ - มันยังไม่ชัดเจนจากคำถามของคุณ ไม่สำคัญว่าคุณจะปิดสตรีมคำขอมากเกินไปหรือไม่ แต่โดยทั่วไปแล้วฉันจะทำเช่นนั้น ฉันจะแก้ไขคำตอบนี้ - ดูเหมือนว่าจะไม่มีการโอเวอร์โหลด
Jon Skeet

1
ฉันเพิ่งทำสิ่งนี้ตอนนี้: String payLoad = CharStreams.toString (InputStreamReader ใหม่ (request.getInputStream (), "UTF-8"));
Blankman

1
@BeeOnRope: ฉันเดาว่าแนวทางระดับกลางอย่างหนึ่งคือCharsets.UTF_8.name()- ทนต่อการพิมพ์ผิดมากกว่า
Jon Skeet

11

อีกทางเลือกหนึ่งคืออ่านไบต์จากสตรีมและสร้างสตริงจากพวกเขา:

new String(ByteStreams.toByteArray(inputStream))
new String(ByteStreams.toByteArray(inputStream), Charsets.UTF_8)

มันไม่ใช่ฝรั่ง 'บริสุทธิ์' แต่มันสั้นกว่าเล็กน้อย


น่าเสียดายที่ByteStreams.toByteArray()ไม่ได้ปิดสตรีมตาม Javadoc
The Alchemist

นั่นคือเรื่องจริง ยังไม่เห็นฟังก์ชั่นของ Guava ที่ปิดสตรีมเลย ยกเว้น closeQuietly
ponomandr

1
โดยปกติสตรีมจะเปิดในคำสั่ง try-with-resource และปิดโดยอัตโนมัติดังนั้นจึงไม่ควรรับผิดชอบต่อ toByteArray ()
ponomandr

4

จากคำตอบที่ได้รับการยอมรับนี่คือวิธียูทิลิตี้ที่ล้อเลียนพฤติกรรมของIOUtils.toString()(และเวอร์ชันที่โอเวอร์โหลดพร้อมชุดอักขระด้วย) เวอร์ชั่นนี้น่าจะปลอดภัยใช่ไหม?

public static String toString(final InputStream is) throws IOException{
    return toString(is, Charsets.UTF_8);
}


public static String toString(final InputStream is, final Charset cs)
throws IOException{
    Closeable closeMe = is;
    try{
        final InputStreamReader isr = new InputStreamReader(is, cs);
        closeMe = isr;
        return CharStreams.toString(isr);
    } finally{
        Closeables.closeQuietly(closeMe);
    }
}

ดูดีมากสำหรับฉัน สิ่งที่ IO ของ Guava ทำงานได้ดีที่สุดหากคุณเรียนรู้ที่จะคิดในแง่ของซัพพลายเออร์อินพุตที่ใช้ซ้ำได้มากกว่าสตรีมและผู้อ่าน 1-shot (ถ้าเป็นไปได้) แต่ฉันเดาว่าเนื่องจากคุณกำลังแปลงรหัส IOUtils ที่มีอยู่ซึ่งจะเป็นการเปลี่ยนแปลงครั้งใหญ่
ColinD

2
ในฝรั่ง 14 ของฉัน closeQuietly เลิกใช้แล้ว ข้อเสนอแนะคือให้ใช้คุณลักษณะ try-with-resources ที่มีอยู่ใน Java 7 ดูข้อมูลเพิ่มเติมได้ที่code.google.com/p/guava-libraries/wiki/…
bertie

2
@AlbertKam เห็นด้วย แต่จำไว้ว่าคำตอบนี้มีอายุสามขวบ
Sean Patrick Floyd

@SeanPatrickFloyd: ขอบคุณ! อันที่จริงฉันได้พบกับโซลูชันที่ใหม่กว่าโดยเริ่มจากคำตอบของคุณ ฉันคิดว่าจะเพิ่มความคิดเห็นสำหรับคนอื่น ๆ ที่อาจใช้เวอร์ชันใหม่กว่านี้ :)
bertie

4

มีโซลูชันการปิดอัตโนมัติที่สั้นกว่ามากในกรณีที่อินพุตสตรีมมาจากทรัพยากร classpath:

URL resource = classLoader.getResource(path);
byte[] bytes = Resources.toByteArray(resource);
String text = Resources.toString(resource, StandardCharsets.UTF_8);

ใช้ฝรั่งทรัพยากรแรงบันดาลใจจากIOExplained


1
ชั้นเรียนทรัพยากรไม่มีอยู่เมื่อถามคำถามนี้ แต่คุณพูดถูก: วันนี้น่าจะเป็นหนทางไป ขอบคุณ
Sean Patrick Floyd

2

แก้ไข (2015): Okioเป็นนามธรรมและเครื่องมือที่ดีที่สุดสำหรับ I / O ใน Java / Android ที่ฉันรู้จัก ฉันจะใช้มันตลอดเวลา.

FWIW นี่คือสิ่งที่ฉันใช้

หากฉันมีสตรีมอยู่แล้วให้ทำดังนี้

final InputStream stream; // this is received from somewhere
String s = CharStreams.toString(CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
    public InputStream getInput() throws IOException {
        return stream;
    }
}, Charsets.UTF_8));

หากฉันกำลังสร้างสตรีม:

String s = CharStreams.toString(CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
    public InputStream getInput() throws IOException {
        return <expression creating the stream>;
    }
}, Charsets.UTF_8));

ตัวอย่างที่เป็นรูปธรรมฉันสามารถอ่านเนื้อหาไฟล์ข้อความ Android ได้ดังนี้:

final Context context = ...;
String s = CharStreams.toString(CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
    public InputStream getInput() throws IOException {
        return context.getAssets().open("my_asset.txt");
    }
}, Charsets.UTF_8));

เลิกใช้งานทั้งหมดแล้ว :(
user3562927

1
ลองใช้github.com/square/okioแทน - ฉันไม่ได้ใช้ I / O ของ Guava มาระยะหนึ่งแล้ว Okio ดีขึ้นกว่าเดิม
orip

0

ตัวอย่างที่เป็นรูปธรรมนี่คือวิธีที่ฉันสามารถอ่านเนื้อหาไฟล์ข้อความของ Android:

public static String getAssetContent(Context context, String file) {
    InputStreamReader reader = null;
    InputStream stream = null;
    String output = "";

    try {
        stream = context.getAssets().open(file);
        reader = new InputStreamReader(stream, Charsets.UTF_8);
        output = CharStreams.toString(reader);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

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