ฉันจะรับ java.io.InputStream จาก java.lang.String ได้อย่างไร


95

ฉันมีStringที่ฉันต้องการใช้เป็นInputStreamไฟล์. ใน Java 1.0 คุณสามารถใช้ได้java.io.StringBufferInputStreamแต่นั่นเป็น@Deprecrated(ด้วยเหตุผลที่ดี - คุณไม่สามารถระบุการเข้ารหัสชุดอักขระได้):

คลาสนี้แปลงอักขระเป็นไบต์ไม่ถูกต้อง สำหรับ JDK 1.1 วิธีที่ต้องการในการสร้างสตรีมจากสตริงคือผ่านStringReader คลาส

คุณสามารถสร้างjava.io.Readerด้วยjava.io.StringReaderแต่ไม่มีอะแดปเตอร์ที่จะใช้Readerและสร้างInputStreamไฟล์.

ฉันพบแมงโบราณขอให้มีการเปลี่ยนทดแทนที่เหมาะสม แต่ไม่มีสิ่งนั้นอยู่ - เท่าที่ฉันสามารถบอกได้

วิธีแก้ปัญหาที่แนะนำบ่อย ๆ คือใช้java.lang.String.getBytes()เป็นข้อมูลเข้าjava.io.ByteArrayInputStream:

public InputStream createInputStream(String s, String charset)
    throws java.io.UnsupportedEncodingException {

    return new ByteArrayInputStream(s.getBytes(charset));
}

แต่นั่นหมายถึงการทำให้ข้อมูลทั้งหมดStringในหน่วยความจำกลายเป็นอาร์เรย์ของไบต์และเอาชนะจุดประสงค์ของสตรีม ในกรณีส่วนใหญ่นี่ไม่ใช่เรื่องใหญ่ แต่ฉันกำลังมองหาสิ่งที่จะรักษาจุดประสงค์ของสตรีม - ข้อมูลที่มีเพียงเล็กน้อยที่สุดเท่าที่จะเป็นไปได้คือ (re) ปรากฏในหน่วยความจำ

คำตอบ:


78

อัปเดต:คำตอบนี้เป็นสิ่งที่ OP ไม่ต้องการอย่างแน่นอน โปรดอ่านคำตอบอื่น ๆ

สำหรับกรณีเหล่านั้นเมื่อเราไม่สนใจว่าข้อมูลจะถูกทำให้เป็นรูปเป็นร่างขึ้นใหม่ในหน่วยความจำโปรดใช้:

new ByteArrayInputStream(str.getBytes("UTF-8"))

3
คำตอบที่เสนอโดยคำตอบนี้ได้รับการคาดหวังไตร่ตรองและปฏิเสธโดยคำถาม ดังนั้นในความคิดของฉันคำตอบนี้ควรถูกลบทิ้ง
Mike Nakis

1
คุณอาจจะพูดถูก เดิมทีฉันแสดงความคิดเห็นอาจเป็นเพราะมันไม่ใช่คำตอบที่แท้จริงสำหรับคำถามของ OP
Andres Riofrio

28
ในฐานะผู้เยี่ยมชมที่มาที่นี่เนื่องจากชื่อคำถามฉันรู้สึกยินดีที่มีคำตอบอยู่ที่นี่ ดังนั้น: โปรดอย่าลบคำตอบนี้ ข้อสังเกตด้านบน "คำตอบนี้เป็นสิ่งที่ OP ไม่ต้องการโปรดอ่านคำตอบอื่น ๆ " เพียงพอแล้ว
Yaakov Belch

10
ณ java7:new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))
ช้า

19

หากคุณไม่ทราบการพึ่งพาที่คอมมอนส์-ioแพคเกจแล้วคุณสามารถใช้IOUtils.toInputStream (String ข้อความ)วิธีการ


11
ในกรณีนั้นคุณจะเพิ่มการอ้างอิงซึ่งไม่ได้ทำอะไรเลยนอกจาก 'ส่งคืน ByteArrayInputStream ใหม่ (input.getBytes ());' การพึ่งพานั้นคุ้มค่าจริงหรือ? ด้วยความสัตย์จริงไม่ - มันไม่ใช่
whaefelinger

3
จริงอยู่นอกจากนี้มันเป็นวิธีแก้ปัญหาที่แน่นอนแล้ว op ก็ไม่อยากใช้เพราะเขาไม่ต้องการ "ทำให้สตริงเป็นรูปเป็นร่างลงในหน่วยความจำ" ที่เปิดให้สตริงปรากฏขึ้นที่อื่นในระบบ :)
Fotis Paraskevopoulos

เรามีไลบรารีที่แปลงออบเจ็กต์ที่กำหนดเองเป็นแหล่งอินพุตสตรีมหรือไม่ บางอย่างเช่น IOUtils.toInputStream (วัตถุ MyObject)?
nawazish-stackoverflow

5

มีอะแดปเตอร์จาก Apache Commons-IO เป็นซึ่งปรับจาก Reader เพื่อ InputStream ซึ่งเป็นชื่อReaderInputStream

รหัสตัวอย่าง:

@Test
public void testReaderInputStream() throws IOException {
    InputStream inputStream = new ReaderInputStream(new StringReader("largeString"), StandardCharsets.UTF_8);
    Assert.assertEquals("largeString", IOUtils.toString(inputStream, StandardCharsets.UTF_8));
}

อ้างอิง: https://stackoverflow.com/a/27909221/5658642


3

ในความคิดของฉันวิธีที่ง่ายที่สุดในการทำเช่นนี้คือการส่งข้อมูลผ่าน Writer:

public class StringEmitter {
  public static void main(String[] args) throws IOException {
    class DataHandler extends OutputStream {
      @Override
      public void write(final int b) throws IOException {
        write(new byte[] { (byte) b });
      }
      @Override
      public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
      }
      @Override
      public void write(byte[] b, int off, int len)
          throws IOException {
        System.out.println("bytecount=" + len);
      }
    }

    StringBuilder sample = new StringBuilder();
    while (sample.length() < 100 * 1000) {
      sample.append("sample");
    }

    Writer writer = new OutputStreamWriter(
        new DataHandler(), "UTF-16");
    writer.write(sample.toString());
    writer.close();
  }
}

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


อีกทางเลือกหนึ่งในการเขียน CharsetEncoder wrapper ของคุณเองเพื่อใช้ Writer ในการเข้ารหัสข้อมูลแม้ว่าจะเป็นเรื่องที่เจ็บปวดที่ต้องทำ สิ่งนี้ควรเป็นการใช้งานที่เชื่อถือได้ (หากไม่มีประสิทธิภาพ):

/** Inefficient string stream implementation */
public class StringInputStream extends InputStream {

  /* # of characters to buffer - must be >=2 to handle surrogate pairs */
  private static final int CHAR_CAP = 8;

  private final Queue<Byte> buffer = new LinkedList<Byte>();
  private final Writer encoder;
  private final String data;
  private int index;

  public StringInputStream(String sequence, Charset charset) {
    data = sequence;
    encoder = new OutputStreamWriter(
        new OutputStreamBuffer(), charset);
  }

  private int buffer() throws IOException {
    if (index >= data.length()) {
      return -1;
    }
    int rlen = index + CHAR_CAP;
    if (rlen > data.length()) {
      rlen = data.length();
    }
    for (; index < rlen; index++) {
      char ch = data.charAt(index);
      encoder.append(ch);
      // ensure data enters buffer
      encoder.flush();
    }
    if (index >= data.length()) {
      encoder.close();
    }
    return buffer.size();
  }

  @Override
  public int read() throws IOException {
    if (buffer.size() == 0) {
      int r = buffer();
      if (r == -1) {
        return -1;
      }
    }
    return 0xFF & buffer.remove();
  }

  private class OutputStreamBuffer extends OutputStream {

    @Override
    public void write(int i) throws IOException {
      byte b = (byte) i;
      buffer.add(b);
    }

  }

}

2

วิธีหนึ่งที่เป็นไปได้คือ:

  • สร้างไฟล์ PipedOutputStream
  • บีบไปที่ไฟล์ PipedInputStream
  • พันOutputStreamWriterรอบPipedOutputStream (คุณสามารถระบุการเข้ารหัสในตัวสร้างที่)
  • อื่น ๆ สิ่งที่คุณเขียนถึงOutputStreamWriterสามารถอ่านได้จากPipedInputStream!

แน่นอนว่านี่เป็นวิธีที่ค่อนข้างแฮ็ค แต่อย่างน้อยก็เป็นวิธีหนึ่ง


1
น่าสนใจ ... แน่นอนด้วยวิธีนี้ฉันเชื่อว่าคุณอาจจะเป็นจริงทั้งสตริงในความทรงจำหรือต้องทนทุกข์ทรมานจากการอ่านกระทู้ ยังคงหวังว่าจะมีการนำไปใช้จริงที่ไหนสักแห่ง
Jared Oberhaus

5
คุณต้องระวังการสตรีม Piped (Input | Output) ตามเอกสาร: "... ไม่แนะนำให้พยายามใช้อ็อบเจ็กต์ทั้งสองจากเธรดเดียวเนื่องจากอาจทำให้เธรดหยุดชะงัก ... " java.sun.com/j2se/1.4.2/docs/api/java/ io / PipedInputStream.html
Bryan Kyle

1

วิธีแก้ปัญหาคือการม้วนของคุณเองสร้างการInputStreamใช้งานที่น่าจะใช้java.nio.charset.CharsetEncoderในการเข้ารหัสแต่ละcharชิ้นหรือชิ้นส่วนcharเป็นอาร์เรย์ของไบต์InputStreamตามความจำเป็น


1
การทำทีละตัวอักษรมีราคาแพง นั่นเป็นเหตุผลว่าทำไมเราจึงมี "ตัวทำซ้ำแบบเป็นชิ้น ๆ " เช่น InputStream ที่ช่วยให้เราอ่านบัฟเฟอร์ได้ในแต่ละครั้ง
Tom Hawtin - แท

ผมเห็นด้วยกับทอม - คุณจริงๆไม่ต้องการที่จะทำเช่นนี้ตัวละครตัวหนึ่งที่เวลา
Eddie

1
เว้นแต่ว่าข้อมูลจะมีขนาดเล็กมากและสิ่งอื่น ๆ (เช่นเวลาแฝงของเครือข่าย) จะใช้เวลานานขึ้น แล้วมันไม่สำคัญ :)
Andres Riofrio

0

คุณสามารถใช้ความช่วยเหลือของไลบรารี org.hsqldb.lib

public StringInputStream(String paramString)
  {
    this.str = paramString;
    this.available = (paramString.length() * 2);
  }

1
โดยทั่วไปคำถามจะมีประโยชน์กว่ามากหากมีคำอธิบายว่าโค้ดนี้มีไว้เพื่อทำอะไร
Peter

-1

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ฉันก็มีปัญหาเดียวกันในวันนี้และนี่คือวิธีแก้ปัญหาของฉัน:

public static InputStream getStream(final CharSequence charSequence) {
 return new InputStream() {
  int index = 0;
  int length = charSequence.length();
  @Override public int read() throws IOException {
   return index>=length ? -1 : charSequence.charAt(index++);
  }
 };
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.