Як я можу отримати 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

Оновлення: Ця відповідь - саме те, що не хоче ОП. Будь ласка, прочитайте інші відповіді.

Для тих випадків, коли нам байдуже, чи дані ре-матеріалізуються в пам’яті, будь ласка, використовуйте:

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

3
Рішення, запропоноване цією відповіддю, було передбачене, передбачене та відхилене питанням. Тож, на мою думку, цю відповідь слід видалити.
Mike Nakis

1
Ви можете мати рацію. Спочатку я зробив це коментарем, мабуть, тому, що це не була фактичною відповіддю на питання ОП.
Андрес Ріофріо,

28
Як відвідувач, який приїжджає сюди через назву запитання, я радий, що ця відповідь тут. Отже: будь ласка, не видаляйте цю відповідь. Зауваження у верхній частині "Ця відповідь - саме те, що не хоче ОП. Будь ласка, прочитайте інші відповіді". достатньо.
Яков Бельч,

10
Станом на java7:new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))
повільний

19

Якщо ви не проти залежності від пакету commons-io , тоді ви можете скористатися методом IOUtils.toInputStream (Текстовий рядок) .


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, яку я використовую, передає дані в 8K фрагментах, але ви можете вплинути на розмір буфера, зменшивши кількість символів, записаних одночасно, і викликавши флеш.


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

Звичайно, це здається досить шаленим способом зробити це, але принаймні це спосіб.


1
Цікаво ... звичайно, з цим рішенням я вважаю, що ви або матеріалізували б цілий рядок у пам’яті, або страждали від голоду в потоці читання. Все ще сподіваючись, що десь справді є реалізація.
Джаред Обергаус,

5
Ви повинні бути обережними з конвеєрним потоком (вхід | вихід). Відповідно до документів: "... Спроба використовувати обидва об'єкти з одного потоку не рекомендується, оскільки це може заблокувати
Брайан Кайл

1

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


1
Робити речі за одним персонажем дорого. Ось чому у нас є "порвані ітератори", такі як InputStream, які дозволяють нам читати буфер за раз.
Том Хоутін - таклін

Я згоден з Томом - ви дійсно не хочете робити цього одного персонажа за раз.
Едді

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++);
  }
 };
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.