Bagaimana saya bisa mendapatkan java.io.InputStream dari java.lang.String?

95

Saya memiliki Stringyang ingin saya gunakan sebagai file InputStream. Di Java 1.0, Anda dapat menggunakan java.io.StringBufferInputStream, tetapi itu sudah @Deprecrated(dengan alasan yang bagus - Anda tidak dapat menentukan penyandian kumpulan karakter):

Kelas ini tidak mengubah karakter menjadi byte dengan benar. Mulai JDK 1.1, cara yang disukai untuk membuat aliran dari string adalah melalui StringReader kelas.

Anda dapat membuat java.io.Readerdengan java.io.StringReader, tetapi tidak ada adaptor untuk mengambil Readerdan membuat file InputStream.

Saya menemukan bug kuno yang meminta pengganti yang sesuai, tetapi tidak ada yang seperti itu - sejauh yang saya tahu.

Solusi yang sering disarankan adalah dengan menggunakan java.lang.String.getBytes()masukan untuk java.io.ByteArrayInputStream:

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

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

tapi itu berarti mewujudkan seluruh Stringmemori sebagai array byte, dan mengalahkan tujuan streaming. Dalam kebanyakan kasus, ini bukan masalah besar, tetapi saya sedang mencari sesuatu yang akan mempertahankan maksud dari sebuah aliran - bahwa sesedikit mungkin data terwujud (kembali) dalam memori.

Jared Oberhaus
sumber

Jawaban:

78

Pembaruan: Jawaban ini persis seperti yang tidak diinginkan OP. Silakan baca jawaban lainnya.

Untuk kasus-kasus ketika kami tidak peduli tentang data yang terwujud kembali dalam memori, gunakan:

new ByteArrayInputStream(str.getBytes("UTF-8"))
Andres Riofrio
sumber
3
Solusi yang diajukan oleh jawaban ini telah diantisipasi, direnungkan, dan ditolak oleh pertanyaan. Jadi menurut saya, jawaban ini harus dihapus.
Mike Nakis
1
Kamu mungkin benar. Saya awalnya membuat komentar mungkin karena itu bukan jawaban sebenarnya untuk pertanyaan OP.
Andres Riofrio
28
Sebagai pengunjung yang datang ke sini karena judul pertanyaannya, saya senang jawaban ini ada di sini. Jadi: Tolong jangan hapus jawaban ini. Komentar di atas "Jawaban ini tepat yang tidak diinginkan OP. Silakan baca jawaban lainnya." Cukup.
Yaakov Belch
10
Pada java7:new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))
lambat
19

Jika Anda tidak keberatan dengan ketergantungan pada paket commons-io , maka Anda dapat menggunakan metode IOUtils.toInputStream (String teks) .

Fotis Paraskevopoulos
sumber
11
Dalam hal ini, Anda menambahkan dependensi yang tidak melakukan apa-apa selain `return new ByteArrayInputStream (input.getBytes ()); ' Apakah itu benar-benar layak untuk digunakan? Sejujurnya, tidak - tidak.
whaefelinger
3
Benar, selain itu persis solusi yang op tidak ingin digunakan karena dia tidak ingin "mewujudkan string ke dalam memori" menentang string yang terwujud di tempat lain dalam sistem :)
Fotis Paraskevopoulos
Apakah kita memiliki perpustakaan yang mengubah objek kustom menjadi sumber aliran input; sesuatu seperti IOUtils.toInputStream (objek MyObject)?
nawazish-stackoverflow
5

Ada adaptor dari Apache Commons-IO yang menyesuaikan dari Reader ke InputStream, yang diberi nama ReaderInputStream .

Kode contoh:

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

Referensi: https://stackoverflow.com/a/27909221/5658642

mengalahkan
sumber
3

Menurut saya, cara termudah untuk melakukan ini adalah dengan mendorong data melalui 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();
  }
}

Implementasi JVM Saya menggunakan data yang didorong dalam 8K potongan, tetapi Anda dapat memiliki beberapa pengaruh pada ukuran buffer dengan mengurangi jumlah karakter yang ditulis pada satu waktu dan memanggil flush.


Alternatif untuk menulis bungkus CharsetEncoder Anda sendiri untuk menggunakan Writer untuk menyandikan data, meskipun itu adalah sesuatu yang sulit untuk dilakukan dengan benar. Ini harus menjadi implementasi yang andal (jika tidak efisien):

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

  }

}
McDowell
sumber
2

Nah, salah satu cara yang mungkin adalah:

  • Membuat PipedOutputStream
  • Sambungkan ke a PipedInputStream
  • Bungkus di OutputStreamWritersekitar PipedOutputStream(Anda dapat menentukan pengkodean dalam konstruktor)
  • Et voilá, apa pun yang Anda tulis OutputStreamWriterdapat dibaca dari PipedInputStream!

Tentu saja, ini tampak seperti cara yang agak hackish untuk melakukannya, tetapi setidaknya itu adalah cara.

Michael Myers
sumber
1
Menarik ... tentu saja, dengan solusi ini saya yakin Anda akan mewujudkan seluruh string dalam memori, atau menderita kelaparan di thread pembacaan. Masih berharap ada implementasi nyata di suatu tempat.
Jared Oberhaus
5
Anda harus berhati-hati dengan Stream Piped (Input | Output). Sesuai dengan dokumen: "... Mencoba menggunakan kedua objek dari satu utas tidak disarankan, karena dapat membuntu utas ..." java.sun.com/j2se/1.4.2/docs/api/java/ io / PipedInputStream.html
Bryan Kyle
1

Solusinya adalah dengan menggulung sendiri, membuat InputStreamimplementasi yang kemungkinan besar akan digunakan java.nio.charset.CharsetEncoderuntuk menyandikan setiap charatau potongan chars menjadi array byte untuk yang InputStreamdiperlukan.

Jared Oberhaus
sumber
1
Melakukan sesuatu satu karakter pada satu waktu itu mahal. Itulah mengapa kami memiliki "iterator potong" seperti InputStream yang memungkinkan kami membaca buffer pada satu waktu.
Tom Hawtin - tackline
Saya setuju dengan Tom - Anda benar - benar tidak ingin melakukan karakter ini satu per satu.
Eddie
1
Kecuali jika datanya sangat kecil, dan hal lain (latensi jaringan, misalnya) membutuhkan waktu lebih lama. Maka tidak masalah. :)
Andres Riofrio
0

Anda dapat mengambil bantuan dari perpustakaan org.hsqldb.lib.

public StringInputStream(String paramString)
  {
    this.str = paramString;
    this.available = (paramString.length() * 2);
  }
omar
sumber
1
Umumnya, pertanyaan jauh lebih berguna jika menyertakan penjelasan tentang tujuan kode tersebut.
Peter
-1

Saya tahu ini adalah pertanyaan lama tetapi saya sendiri memiliki masalah yang sama hari ini, dan inilah solusi saya:

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++);
  }
 };
}
Paul Richards
sumber