Считывает все стандартные входные данные в массив байтов Java

#java #io #binary #stdin

#java #unix #stdin

Вопрос:

Какой самый простой способ в современной Java (используя только стандартные библиотеки) считывать весь стандартный ввод до EOF в массив байтов, предпочтительно без необходимости самостоятельно предоставлять этот массив? Данные stdin являются двоичными и не поступают из файла.

Т.е. что-то вроде Ruby

 foo = $stdin.read
  

Единственное частичное решение, о котором я мог подумать, было примерно таким

 byte[] buf = new byte[1000000];
int b;
int i = 0;

while (true) {
    b = System.in.read();
    if (b == -1)
        break;
    buf[i  ] = (byte) b;
}

byte[] foo[i] = Arrays.copyOfRange(buf, 0, i);
  

… но это кажется странно подробным даже для Java и использует буфер фиксированного размера.

Ответ №1:

Я бы использовал Guava и его ByteStreams.toByteArray метод:

 byte[] data = ByteStreams.toByteArray(System.in);
  

Без использования каких-либо сторонних библиотек я бы использовал ByteArrayOutputStream и временный буфер:

 ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[32 * 1024];

int bytesRead;
while ((bytesRead = System.in.read(buffer)) > 0) {
    baos.write(buffer, 0, bytesRead);
}
byte[] bytes = baos.toByteArray();
  

… возможно, инкапсулируя это в метод, принимающий InputStream , который в любом случае был бы в основном эквивалентен ByteStreams.toByteArray

Комментарии:

1. Спасибо. Довольно безумно, что чего-то вроде решения Guava нет в mainline stdlib.

2. Причина, по которой это не так, заключается в том, что авторы Java пытаются препятствовать чтению материала в массивы байтов (что приведет к разрыву с достаточно большими данными), когда вместо этого он может / должен обрабатываться как поток (который может обрабатывать неограниченные объемы данных).

Ответ №2:

Если вы читаете из файла, Files.ReadAllBytes — это способ сделать это.

В противном случае я бы использовал ByteBuffer:

 ByteBuffer buf = ByteBuffer.allocate(1000000);
ReadableByteChannel channel = Channels.newChannel(System.in);
while (channel.read(buf) >= 0)
    ;
buf.flip();
byte[] bytes = Arrays.copyOf(buf.array(), buf.limit());
  

Комментарии:

1. Это неправильно, поскольку вы не проверяете buf.hasRemaining()

2. На самом деле, проверка hasRemaining() — плохая идея. Цель состоит в том, чтобы прочитать все байты, поэтому мы не хотим молча прекращать чтение только потому, что мы достигли конца буфера. Исключение лучше, чем обработка усеченных данных, как если бы это были полные данные. Я предположил максимальный размер 1000000, потому что OP кажется удобным с этим предположением.