Преобразование байтового массива в строку без использования оператора new в Java - PullRequest
3 голосов
/ 10 июля 2010

Есть ли способ преобразовать байтовый массив в строку, отличную от new String(bytearray)? Точная проблема в том, что я передаю строку в формате json по сети через соединение UDP. На другом конце я получаю его в байтовом массиве фиксированного размера (поскольку я не знаю о размере массива) и создаю новую строку из байтового массива. Если я сделаю это, вся память, которую я выделил, будет лишней.

Чтобы избежать этого, я получаю байтовый массив, преобразующий его в строку, усекаю строку до последнего действительного символа, а затем преобразую его в байтовый массив и создаю из него новую строку. Если я делаю это, он просто использует необходимую память, но частота сборки мусора становится настолько высокой, что требует большего количества выделений. Каков наилучший способ сделать это?

Ответы [ 4 ]

2 голосов
/ 10 июля 2010

Самый простой и надежный способ сделать это - использовать длину пакета, который вы читаете из сокета UDP.Javadoc для DatagramSocket.receive(...) говорит следующее:

Получает пакет дейтаграммы из этого сокета.Когда этот метод возвращается, буфер DatagramPacket заполняется полученными данными.Пакет дейтаграмм также содержит IP-адрес отправителя и номер порта на машине отправителя.

Этот метод блокируется до получения дейтаграммы. Поле длины объекта пакета дейтаграммы содержит длину принятого сообщения. Если сообщение длиннее, чем длина пакета, сообщение усекается.

Если вы не можете этого сделать, то ниже будет выделена строка минимального размера без ненужного выделения временных.

  byte[] buff = ... // read from socket.

  // Find byte offset of first 'non-character' in buff
  int i;
  for (i = 0; i < buff.length && /* buff[i] represents a character */; i++) { /**/ }

  // Allocate String
  String res = new String(buff, 0, i, charsetName);

Обратите внимание, что критерий для определения не-символаэто набор символов и приложения.Но, вероятно, достаточно проверки на нулевой байт.

РЕДАКТИРОВАТЬ

Что именно означает javadoc под "длина новой строки является функцией кодировки и, следовательно, не может быть равна длине подмассива. "

Это указывает на то, что для некоторых кодировок символов (например UTF-8, UTF-16, JIS и т. д.) некоторые символы представлены двумя или более байтами.Так, например, 10 байтов UTF-8 могут представлять менее 10 символов.

2 голосов
/ 10 июля 2010

Хотелось бы что-то вроде:

String s = new String( bytearray, 0, lenOfValidData, "US-ASCII");

делать то, что вы хотите (изменить кодировку на подходящую кодировку)?


Обновление:

Исходя из ваших комментариев, вы можете попробовать:

socket.receive(packet);
String strPacket = new String( packet.getData(), 0, packet.getLength(), "US-ASCII");
receiver.onReceive( strPacket);

Я недостаточно знаком с поддержкой дейтаграмм Java, чтобы знать, возвращает ли packet.getLength() усеченную длину или исходную длину дейтаграммы (до усечения, чтобы поместиться в приемный буфер). Может быть безопаснее создать строку следующим образом:

String strPacket = new String( packet.getData(), 
                               0, 
                               Math.min( packet.getLength(), packet.getData().length),
                               "US-ASCII");

Опять же, это может быть ненужным.

0 голосов
/ 14 января 2017

Можете ли вы сначала записать входной поток в ByteArrayOutputStream, а затем вызвать toString в выходном потоке?Вот как то так:

ByteArrayOutputStream os = new ByteArrayOutputStream();
while (!socket.isClosed()) {
    InputStream is = socket.getInputStream();
    byte[] buffer = new byte[1024]; // some tmp buffer.  Define the appropriate size here
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
        baos.write(buffer, 0, bytesRead);
        if (is.available() <= 0) {
            break;
        }
    }
    System.out.println(baos.toString());
    baos.reset();
}
0 голосов
/ 10 июля 2010

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

  1. Получить байтовый массив (фиксированного размера) на стороне клиента.
  2. Создать объект StringBuilder.
  3. Loopпо массиву, пока вы читаете допустимые символы и добавляете их к объекту StringBuilder.
  4. Массив байтов теперь можно выбросить.(Я бы предпочел сохранить это, хотя в следующий раз вы получите что-то по сети, чтобы избежать ненужного выделения памяти.)
Редактировать

Я последовал предложению Tofubeer использовать StringBuilder вместо StringBuffer .

...