Come posso ottenere un java.io.InputStream da un java.lang.String?


95

Ho un file Stringche voglio usare come file InputStream. In Java 1.0, potresti usare java.io.StringBufferInputStream, ma è stato @Deprecrated(con una buona ragione - non puoi specificare la codifica del set di caratteri):

Questa classe non converte correttamente i caratteri in byte. A partire da JDK 1.1, il modo preferito per creare un flusso da una stringa è tramite la StringReader classe.

Puoi creare un java.io.Readercon java.io.StringReader, ma non ci sono adattatori per prendere un Readere creare un file InputStream.

Ho trovato un antico bug che chiedeva un sostituto adatto, ma non esiste nulla del genere, per quanto ne so.

La soluzione alternativa suggerita è quella di utilizzare java.lang.String.getBytes()come input per java.io.ByteArrayInputStream:

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

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

ma ciò significa materializzare l'intero Stringin memoria come un array di byte e vanifica lo scopo di un flusso. Nella maggior parte dei casi questo non è un grosso problema, ma stavo cercando qualcosa che preservasse l'intento di un flusso - che il minor numero possibile di dati venga (ri) materializzato nella memoria.

Risposte:


78

Aggiornamento: questa risposta è esattamente ciò che l'OP non vuole. Si prega di leggere le altre risposte.

Per quei casi in cui non ci interessa che i dati vengano rimaterializzati in memoria, si prega di utilizzare:

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

3
La soluzione proposta da questa risposta è stata anticipata, contemplata e rifiutata dalla domanda. Quindi, a mio parere, questa risposta dovrebbe essere cancellata.
Mike Nakis

1
Potresti avere ragione. Inizialmente l'ho fatto un commento probabilmente perché non era una risposta effettiva alla domanda di OP.
Andres Riofrio

28
Come visitatore che viene qui a causa del titolo della domanda, sono felice che questa risposta sia qui. Quindi: per favore non cancellare questa risposta. L'osservazione in alto "Questa risposta è esattamente ciò che l'OP non vuole. Si prega di leggere le altre risposte." è sufficiente.
Yaakov Belch

10
A partire da java7:new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))
lento

19

Se non ti dispiace una dipendenza dal pacchetto commons-io , puoi usare il metodo IOUtils.toInputStream (String text) .


11
In questo caso aggiungi una dipendenza che non fa altro che `return new ByteArrayInputStream (input.getBytes ()); ' Vale davvero la pena dipendere? In tutta onestà, no, non lo è.
whaefelinger

3
Vero, inoltre è esattamente la soluzione alternativa che l'op non vuole usare perché non vuole "materializzare la stringa in memoria" invece che la stringa venga materializzata da qualche altra parte nel sistema :)
Fotis Paraskevopoulos

Abbiamo una libreria che converte l'oggetto personalizzato nella sorgente del flusso di input; qualcosa come IOUtils.toInputStream (oggetto MyObject)?
nawazish-stackoverflow

5

C'è un adattatore da Apache Commons-IO che si adatta da Reader a InputStream, che si chiama ReaderInputStream .

Codice di esempio:

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

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


3

A mio avviso, il modo più semplice per farlo è spingere i dati attraverso un 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();
  }
}

L'implementazione JVM che sto usando ha trasferito i dati in blocchi di 8K, ma potresti avere qualche effetto sulla dimensione del buffer riducendo il numero di caratteri scritti contemporaneamente e chiamando flush.


Un'alternativa alla scrittura del proprio wrapper CharsetEncoder per utilizzare un writer per codificare i dati, sebbene sia una seccatura farlo bene. Questa dovrebbe essere un'implementazione affidabile (se inefficiente):

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

Bene, un modo possibile è:

  • Creare un PipedOutputStream
  • Collegalo a un file PipedInputStream
  • Avvolgere un OutputStreamWriterintorno PipedOutputStream(è possibile specificare la codifica nel costruttore)
  • Et voilá, tutto ciò che scrivi OutputStreamWriterpuò essere letto dal PipedInputStream!

Certo, questo sembra un modo piuttosto hacker per farlo, ma almeno è un modo.


1
Interessante ... certo, con questa soluzione credo che materializzeresti l'intera stringa in memoria o soffriresti la fame nel thread di lettura. Spero ancora che ci sia una reale implementazione da qualche parte.
Jared Oberhaus

5
Devi stare attento con Piped (Input | Output) Stream. Secondo la documentazione: "... Il tentativo di utilizzare entrambi gli oggetti da un singolo thread non è consigliato, poiché potrebbe bloccare il thread ..." java.sun.com/j2se/1.4.2/docs/api/java/ io / PipedInputStream.html
Bryan Kyle,

1

Una soluzione è eseguire il rollio personalizzato, creando InputStreamun'implementazione che probabilmente utilizzerebbe java.nio.charset.CharsetEncoderper codificare ciascuno charo un blocco di messaggi di posta elettronica charin un array di byte per InputStreamquanto necessario.


1
Fare le cose un personaggio alla volta è costoso. Ecco perché abbiamo "iteratori a blocchi" come InputStream che ci consentono di leggere un buffer alla volta.
Tom Hawtin - tackline

Sono d'accordo con Tom - davvero non vuoi fare questo personaggio alla volta.
Eddie,

1
A meno che i dati non siano veramente piccoli e altre cose (la latenza di rete, ad esempio) richiedano più tempo. Allora non importa. :)
Andres Riofrio

0

Puoi chiedere aiuto alla libreria org.hsqldb.lib.

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

1
In generale, le domande sono molto più utili se includono una spiegazione di ciò che il codice intende fare.
Peter

-1

So che questa è una vecchia domanda, ma oggi ho avuto lo stesso problema e questa era la mia soluzione:

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++);
  }
 };
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.