Создание потока байтов [дубликат]

Что вам нужно знать:

    текст, который представляет несколько строк, также содержит непечатаемые символы между строками (мы называем их разделителями строк), как возврат каретки (CR - в строковых литералах, представленных как "\r") line line (LF - в строковых литералах, представленных как "\n")
  • , когда вы читаете данные с консоли, это позволяет пользователю вводить свой ответ, и когда он будет сделан, ему необходимо как-то подтверждают этот факт. Для этого пользователю необходимо нажать клавишу «enter» / «return» на клавиатуре. Важно то, что этот ключ, помимо обеспечения размещения пользовательских данных на стандартном входе (представленный System.in, который читается Scanner), также отправляет зависимые от ОС разделители строк (например, для Windows \r\n) после этого. Поэтому, когда вы запрашиваете у пользователя значение типа age, а пользовательские типы 42 и нажимаете enter, стандартный вход будет содержать "42\r\n".

Проблема

Scanner#nextInt (и другие методы Scanner#nextType) не позволяют сканеру потреблять эти разделители строк. Он прочитает их из System.in (как еще Сканер узнает, что больше нет цифр от пользователя, которые представляют age значение, чем перед пробелом?), Который удалит их со стандартного ввода, но он также будет кешем эти разделители строк внутри. Нам нужно помнить, что все методы Scanner всегда сканируются, начиная с кэшированного текста.

Теперь Scanner#nextLine() просто собирает и возвращает все символы , пока не найдет разделители строк (или конец потока). Но поскольку разделители строк после прочтения номера из консоли сразу обнаруживаются в кеше сканера, он возвращает пустую строку, что означает, что сканер не смог найти символ до этих разделителей строк (или конца потока). BTW nextLine также потребляет эти разделители строк.

Решение

Поэтому, когда вы хотите запросить номер, а затем для всей строки, избегая этой пустой строки в результате nextLine, либо

  • потребляет разделитель строк слева от nextInt из кеша сканеров, вызывая nextLine, или вызывая skip("\\R"), чтобы позволить Scanner пропускать часть, сопоставляемую с \R, которая представляет разделитель строк (подробнее о \R: https : //stackoverflow.com/a/31060125 )
  • вообще не используют nextInt (ни next, ни какие-либо методы nextTYPE). Вместо этого прочитайте целые строки данных по строке nextLine и номера разбора из каждой строки (при условии, что одна строка содержит только одно число) для правильного типа, например int, через Integer.parseInt.

BTW: Scanner#nextType методы могут пропускать разделители (по умолчанию все пробелы, такие как вкладки, разделители строк), в том числе кэшированные сканером, пока они не найдут следующее значение без разделителя (токен). Благодаря этому для ввода типа "42\r\n\r\n321\r\n\r\n\r\nfoobar" код

int num1 = sc.nextInt();
int num2 = sc.nextInt();
String name = sc.next();

сможет правильно назначить num1=42 num2=321 name=foobar.

24
задан Tunaki 8 September 2015 в 13:59
поделиться

2 ответа

Нет, этого не существует. Фактически, он явно не реализован, чтобы не загромождать Stream API с тоннами классов для каждого примитивного типа.

Выставлять почту из Брайана Гетца в списке рассылки OpenJDK: & nbsp;

Краткий ответ: нет.

Не стоит использовать еще 100K + для каждого формата JDK для этих форм, которые используются почти никогда. И если бы мы добавили их, кто-то потребовал бы коротких, плавающих или булевых.

Другими словами, если бы люди настаивали, что у нас были все примитивные специализации, у нас не было бы примитивных специализаций. Это будет хуже, чем статус-кво.

27
ответ дан danorton2 24 August 2018 в 16:31
поделиться

Большинство операций, связанных с байтом, автоматически повышаются до int. Например, рассмотрим простой метод, который добавляет константу byte к каждому элементу массива byte[], возвращающему новый массив byte[] (потенциальный кандидат для ByteStream):

public static byte[] add(byte[] arr, byte addend) {
    byte[] result = new byte[arr.length];
    int i=0;
    for(byte b : arr) {
        result[i++] = (byte) (b+addend);
    }
    return result;
}

См. даже если мы выполняем добавление двух переменных byte, они расширяются до int, и вам нужно вернуть результат в byte. В байт-коде Java большинство операций, связанных с byte (кроме загрузки / хранения массива и байт-байт), выражаются с помощью 32-разрядных целых инструкций (iadd, ixor, if_icmple и т. Д.). Таким образом, практически нормально обрабатывать байты как int с IntStream. Нам просто нужны две дополнительные операции:

  • Создайте IntStream из массива byte[] (расширяя байты до int)
  • Соберите массив IntStream в byte[] (используя (byte) литье)

Первый из них очень прост и может быть реализован следующим образом:

public static IntStream intStream(byte[] array) {
    return IntStream.range(0, array.length).map(idx -> array[idx]);
}

Таким образом, вы можете добавить такой статический метод к своему проект и быть счастливым.

Сбор потока в массив byte[] более сложный. Использование стандартных классов JDK самым простым решением является ByteArrayOutputStream:

public static byte[] toByteArray(IntStream stream) {
    return stream.collect(ByteArrayOutputStream::new, (baos, i) -> baos.write((byte) i),
            (baos1, baos2) -> baos1.write(baos2.toByteArray(), 0, baos2.size()))
            .toByteArray();
}

Однако из-за синхронизации у него нет лишних накладных расходов. Также было бы неплохо специально обработать потоки известной длины, чтобы уменьшить выделение и копирование. Тем не менее теперь вы можете использовать Stream API для массивов byte[]:

public static byte[] addStream(byte[] arr, byte addend) {
    return toByteArray(intStream(arr).map(b -> b+addend));
}

В моей библиотеке StreamEx есть обе эти операции в IntStreamEx , который улучшает стандарт IntStream, поэтому вы можете использовать его следующим образом:

public static byte[] addStreamEx(byte[] arr, byte addend) {
    return IntStreamEx.of(arr).map(b -> b+addend).toByteArray();
}

Метод internally toByteArray() использует простой изменяемый размер байт-буфер и специально обрабатывает случай, когда поток является последовательным, и размер цели известен заранее.

31
ответ дан Tagir Valeev 24 August 2018 в 16:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: