Jak mogę uzyskać java.io.InputStream z java.lang.String?


95

Mam plik String, którego chcę użyć jako InputStream. W Javie 1.0 możesz użyć java.io.StringBufferInputStream, ale tak było @Deprecrated(nie bez powodu - nie możesz określić kodowania zestawu znaków):

Ta klasa nie konwertuje poprawnie znaków na bajty. Począwszy od JDK 1.1, preferowanym sposobem tworzenia strumienia z łańcucha jest użycie StringReader klasy.

Możesz utworzyć za java.io.Readerpomocą java.io.StringReader, ale nie ma adapterów do wzięcia Readeri utworzenia InputStream.

Znalazłem starożytny błąd proszący o odpowiednią wymianę, ale nic takiego nie istnieje - o ile wiem.

Często sugerowanym rozwiązaniem jest użycie java.lang.String.getBytes()jako danych wejściowych do java.io.ByteArrayInputStream:

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

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

ale to oznacza materializację całości Stringw pamięci jako tablicę bajtów i udaremnia cel strumienia. W większości przypadków nie jest to wielka sprawa, ale szukałem czegoś, co zachowałoby intencję strumienia - aby jak najmniej danych było (ponownie) zmaterializowanych w pamięci.

Odpowiedzi:


78

Aktualizacja: ta odpowiedź jest dokładnie tym, czego OP nie chce. Przeczytaj pozostałe odpowiedzi.

W przypadkach, gdy nie zależy nam na ponownym zmaterializowaniu danych w pamięci, użyj:

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

3
Rozwiązanie zaproponowane w tej odpowiedzi było antycypowane, rozważane i odrzucane w pytaniu. Więc moim zdaniem ta odpowiedź powinna zostać usunięta.
— Mike Nakis,

1
Możesz mieć rację. Pierwotnie napisałem to jako komentarz prawdopodobnie dlatego, że nie była to właściwa odpowiedź na pytanie OP.
— Andres Riofrio,

28
Jako gość przyjeżdżający tutaj ze względu na tytuł pytania, cieszę się, że ta odpowiedź jest tutaj. Więc: proszę, nie usuwaj tej odpowiedzi. Uwaga u góry „Tej odpowiedzi nie chce OP. Przeczytaj pozostałe odpowiedzi”. jest wystarczający.
— Yaakov Belch

10
Od java7:new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))
— wolno

19

Jeśli nie masz nic przeciwko zależności od pakietu commons-io , możesz użyć metody IOUtils.toInputStream (tekst String) .


11
W takim przypadku dodajesz zależność, która nie robi nic poza `return new ByteArrayInputStream (input.getBytes ()); ' Czy to naprawdę warte zależności? Szczerze mówiąc, nie - tak nie jest.
— whaefelinger

3
To prawda, poza tym jest to dokładnie obejście, którego op nie chce użyć, ponieważ nie chce "zmaterializować struny w pamięci" w przeciwieństwie do tego, że struna materializuje się gdzie indziej w systemie :)
— Fotis Paraskevopoulos

Czy mamy jakąś bibliotekę, która konwertuje niestandardowy obiekt na źródło strumienia wejściowego; coś w rodzaju IOUtils.toInputStream (obiekt MyObject)?
— nawazish-stackoverflow

5

Istnieje adapter firmy Apache Commons-IO, który dostosowuje się z programu Reader do InputStream i nazywa się ReaderInputStream .

Przykładowy kod:

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

Źródła: https://stackoverflow.com/a/27909221/5658642


3

Moim zdaniem najłatwiejszym sposobem na to jest przepchnięcie danych przez moduł 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();
  }
}

Implementacja JVM Używam danych wypychanych w porcjach 8K, ale możesz mieć pewien wpływ na rozmiar bufora, zmniejszając liczbę znaków zapisywanych jednocześnie i wywołując flush.


Alternatywa dla pisania własnego opakowania CharsetEncoder w celu użycia Writera do kodowania danych, chociaż jest to trochę trudne. Powinno to być niezawodne (jeśli nieefektywne) wdrożenie:

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

Cóż, jednym z możliwych sposobów jest:

  • Stwórz PipedOutputStream
  • Przełóż to do PipedInputStream
  • Zawiń OutputStreamWriterwokół PipedOutputStream(możesz określić kodowanie w konstruktorze)
  • Et voilá, wszystko, do czego napiszesz, OutputStreamWritermożna przeczytać w PipedInputStream!

Oczywiście wydaje się to raczej hakerskim sposobem na zrobienie tego, ale przynajmniej tak jest.


1
Ciekawe ... oczywiście, dzięki temu rozwiązaniu uważam, że albo zmaterializowałbyś cały ciąg w pamięci, albo cierpiałby głód na wątku czytającym. Wciąż mam nadzieję, że gdzieś jest prawdziwa implementacja.
— Jared Oberhaus

5
Musisz uważać na strumień potokowy (wejście | wyjście). Zgodnie z dokumentacją: „… Próba użycia obu obiektów z jednego wątku nie jest zalecana, ponieważ może to zablokować wątek…” java.sun.com/j2se/1.4.2/docs/api/java/ io / PipedInputStream.html
— Bryan Kyle

1

Rozwiązaniem jest utworzenie własnego, tworząc InputStreamimplementację, która prawdopodobnie java.nio.charset.CharsetEncoderużyłaby do zakodowania każdego charlub fragmentu chars do tablicy bajtów w InputStreamrazie potrzeby.


1
Robienie rzeczy pojedynczo jest kosztowne. Dlatego mamy „podzielone na fragmenty iteratory”, takie jak InputStream, które pozwalają nam jednocześnie odczytywać bufor.
— Tom Hawtin - tackline

Zgadzam się z Tomem - naprawdę nie chcesz robić tej jednej postaci na raz.
— Eddie

1
Chyba że dane są naprawdę małe, a inne rzeczy (na przykład opóźnienie sieci) trwają dłużej. Wtedy to nie ma znaczenia. :)
— Andres Riofrio

0

Możesz skorzystać z pomocy biblioteki org.hsqldb.lib.

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

1
Ogólnie pytania są znacznie bardziej przydatne, jeśli zawierają wyjaśnienie, do czego służy kod.
— Peter

-1

Wiem, że to stare pytanie, ale sam miałem dzisiaj ten sam problem, a to było moje rozwiązanie:

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++);
  }
 };
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.