Bir java.lang.String'den java.io.InputStream'i nasıl edinebilirim?


95

Kullanmak Stringistediğim bir InputStream. Java 1.0'da kullanabilirsiniz java.io.StringBufferInputStream, ancak bunu kullanabilirsiniz @Deprecrated(iyi bir nedenle - karakter seti kodlamasını belirleyemezsiniz):

Bu sınıf, karakterleri düzgün şekilde bayta dönüştürmez. JDK 1.1'den itibaren, bir dizeden akım yaratmanın tercih edilen yolu StringReader sınıf yoluyladır.

Bir java.io.Readerile oluşturabilirsiniz java.io.StringReader, ancak Readerbir InputStream.

Uygun bir yedek isteyen eski bir böcek buldum , ancak söyleyebileceğim kadarıyla böyle bir şey yok.

Sıklıkla önerilen geçici çözüm, aşağıdakiler için java.lang.String.getBytes()girdi olarak kullanmaktır java.io.ByteArrayInputStream:

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

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

ancak bu String, bellekteki bütünün bir bayt dizisi olarak somutlaştırılması anlamına gelir ve bir akışın amacını bozar. Çoğu durumda bu büyük bir sorun değil, ancak bir akışın amacını koruyacak bir şey arıyordum - mümkün olduğunca az verinin bellekte (yeniden) somutlaştırılması.

Yanıtlar:


78

Güncelleme: Bu cevap tam olarak OP'nin istemediği şeydir. Lütfen diğer cevapları okuyun.

Verilerin bellekte yeniden gerçekleştirilmesini önemsemediğimiz durumlar için lütfen şunu kullanın:

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

3
Bu cevabın önerdiği çözüm, soru tarafından tahmin edilmiş, üzerinde düşünülmüş ve reddedilmiştir. Yani bence bu cevabın silinmesi gerekiyor.
Mike Nakis

1
Haklı olabilirsin. Başlangıçta, muhtemelen OP'nin sorusuna gerçek bir cevap olmadığı için bir yorum yaptım.
Andres Riofrio

28
Soru başlığı nedeniyle buraya gelen bir ziyaretçi olarak, bu cevabın burada olmasına sevindim. Yani: Lütfen bu cevabı silmeyin. Üstteki açıklama "Bu cevap tam olarak OP'nin istemediği şeydir. Lütfen diğer cevapları okuyun." yeterlidir.
Yaakov Belch

10
Java7 itibariyle:new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))
yavaş

19

Commons-io paketine bağımlılık sorun yaşamıyorsanız , IOUtils.toInputStream (String text) yöntemini kullanabilirsiniz.


11
Bu durumda, `` return new ByteArrayInputStream (input.getBytes ()); 'den başka bir şey yapmayan bir bağımlılık eklersiniz; Bu gerçekten bağımlı olmaya değer mi? Dürüst olmak gerekirse, hayır - öyle değil.
whaefelinger

3
Doğru, bunun yanı sıra, operasyonun kullanmayacağı bir geçici çözüm, çünkü dizgenin sistemde başka bir yerde gerçekleştirilmesine karşın "dizeyi hafızaya almak" istemiyor :)
Fotis Paraskevopoulos

Özel nesneyi giriş akışı kaynağına dönüştüren herhangi bir kitaplığımız var mı; IOUtils.toInputStream (MyObject nesnesi) gibi bir şey?
nawazish-stackoverflow


3

Bence bunu yapmanın en kolay yolu, verileri bir Yazar aracılığıyla aktarmaktır:

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 uygulaması, 8K öbeklerde itilen verileri kullanıyorum, ancak bir seferde yazılan karakter sayısını azaltarak ve floş çağırarak arabellek boyutu üzerinde bir miktar etkiye sahip olabilirsiniz.


Verileri kodlamak için bir Writer kullanmak için kendi CharsetEncoder sarmalayıcınızı yazmaya bir alternatif, ancak bu doğru yapılması gereken bir acıdır. Bu, güvenilir (verimsizse) bir uygulama olmalıdır:

/** 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

Olası bir yol şudur:

Tabii ki, bu, bunu yapmanın oldukça hilekâr bir yolu gibi görünüyor, ama en azından bir yol.


1
İlginç ... elbette, bu çözümle, ya tüm diziyi bellekte somutlaştıracağına ya da okuma dizisinde açlık çekeceğine inanıyorum. Yine de bir yerlerde gerçek bir uygulama olduğunu umuyorum.
Jared Oberhaus

5
Borulu (Giriş | Çıkış) Akış konusunda dikkatli olmalısınız. Dokümanlara göre: "... Her iki nesneyi de tek bir iş parçacığından kullanmaya çalışmak, iş parçacığını kilitleyebileceğinden
Bryan Kyle

1

Bir çözüm, gerekli olduğu gibi bir bayt dizisine her bir s parçasını veya s kümesini kodlamak için InputStreamkullanacak bir uygulama oluşturarak kendi çözümünüzü yuvarlamaktır .java.nio.charset.CharsetEncodercharcharInputStream


1
İşleri her seferinde bir karakter yapmak pahalıdır. Bu nedenle, InputStream gibi, bir seferde bir arabelleği okumamıza izin veren "yığın halinde yineleyicilerimiz" var.
Tom Hawtin - tackline

Sana katılıyorum Tom - Eğer gerçekten bir zamanda bu bir karakter yapmak istemiyoruz.
Eddie

1
Veriler gerçekten küçük olmadığı ve diğer şeyler (örneğin ağ gecikmesi) daha uzun sürmediği sürece. O zaman önemli değil. :)
Andres Riofrio

0

Org.hsqldb.lib kütüphanesinden yardım alabilirsiniz.

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

1
Genel olarak, kodun ne yapması amaçlandığına dair bir açıklama içeriyorsa sorular çok daha yararlıdır.
Peter

-1

Bunun eski bir soru olduğunu biliyorum ama bugün de aynı sorunu yaşadım ve bu benim çözümümdü:

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++);
  }
 };
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.