Как я могу получить 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в памяти в виде массива байтов и лишает смысла поток. В большинстве случаев это не имеет большого значения, но я искал что-то, что сохраняло бы намерение потока - чтобы как можно меньше данных (ре) материализовалось в памяти.

Джаред Оберхаус
источник

Ответы:

78

Обновление: этот ответ - именно то, чего OP не хочет. Прочтите, пожалуйста, другие ответы.

В тех случаях, когда нам не важно, чтобы данные повторно материализовались в памяти, используйте:

new ByteArrayInputStream(str.getBytes("UTF-8"))
Андрес Риофрио
источник
3
Решение, предложенное этим ответом, было предвидено, обдумано и отвергнуто вопросом. Так что, на мой взгляд, этот ответ следует удалить.
Майк Накис
1
Возможно, ты прав. Первоначально я сделал это комментарием, вероятно, потому, что это не был фактический ответ на вопрос OP.
Andres Riofrio
28
Как посетитель, приходящий сюда из-за названия вопроса, я рад, что здесь есть этот ответ. Итак: пожалуйста, не удаляйте этот ответ. Замечание вверху: «Этот ответ как раз то, чего ОП не хочет. Прочтите, пожалуйста, другие ответы». достаточно.
Yaakov Belch
10
По состоянию на java7:new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))
медленно
19

Если вы не возражаете против зависимости от пакета commons-io , вы можете использовать метод IOUtils.toInputStream (String text) .

Фотис Параскевопулос
источник
11
В этом случае вы добавляете зависимость, которая не делает ничего, кроме `return new ByteArrayInputStream (input.getBytes ()); ' Это действительно стоит зависимости? Честно говоря, нет - это не так.
whaefelinger
3
Правда, кроме того, это именно обходной путь, который оператор не хочет использовать, потому что он не хочет «материализовать строку в память», противоположную строке, материализуемой где-то еще в системе :)
Фотис Параскевопулос,
Есть ли у нас библиотека, которая преобразует пользовательский объект в источник входного потока; что-то вроде 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 я использую проталкиваемые данные в блоках по 8 КБ, но вы можете повлиять на размер буфера, уменьшив количество символов, записываемых за один раз, и вызвав flush.


Альтернатива написанию собственной оболочки CharsetEncoder, использующей 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(вы можете указать кодировку в конструкторе)
  • Et voilá, все, что вы пишете, OutputStreamWriterможно прочитать из PipedInputStream!

Конечно, это кажется довольно хакерским способом сделать это, но, по крайней мере, это способ.

Майкл Майерс
источник
1
Интересно ... конечно, я считаю, что с этим решением вы либо материализуете всю строку в памяти, либо будете страдать от голода в потоке чтения. Все еще надеюсь, что где-то есть настоящая реализация.
Джаред Оберхаус
5
Вы должны быть осторожны с потоком Piped (Input | Output). В соответствии с документами: «... Попытка использовать оба объекта из одного потока не рекомендуется, так как это может заблокировать
Брайан Кайл
1

Решение состоит в том, чтобы свернуть свое собственное, создав InputStreamреализацию, которая, вероятно, будет использовать java.nio.charset.CharsetEncoderдля кодирования каждого charили фрагмента chars в массив байтов по InputStreamмере необходимости.

Джаред Оберхаус
источник
1
Делать что-то по одному персонажу дорого. Вот почему у нас есть «фрагментированные итераторы», такие как InputStream, которые позволяют нам читать буфер за раз.
Том Хотин - tackline
Я согласен с Томом - вы действительно не хотите делать этого по одному персонажу за раз.
Эдди
1
Если данных действительно мало, и другие вещи (например, задержка в сети) занимают больше времени. Тогда это не имеет значения. :)
Андрес Риофрио
0

Вы можете воспользоваться помощью библиотеки org.hsqldb.lib.

public StringInputStream(String paramString)
  {
    this.str = paramString;
    this.available = (paramString.length() * 2);
  }
омар
источник
1
Как правило, вопросы гораздо полезнее, если они включают объяснение того, для чего предназначен код.
Питер
-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++);
  }
 };
}
Пол Ричардс
источник