Java ByteBuffer в строку

122

Это правильный подход для преобразования ByteBuffer в String таким образом,

String k = "abcd";
ByteBuffer b = ByteBuffer.wrap(k.getBytes());
String v = new String(b.array());

if(k.equals(v))
    System.out.println("it worked");
else
    System.out.println("did not work");

Причина, по которой я спрашиваю, заключается в том, что это выглядит слишком просто, тогда как другие подходы, такие как Java: преобразование String в ByteBuffer и из него, и связанные с этим проблемы выглядят более сложными.

vikky.rk
источник
3
Ну что, пробовали?
tckmn
6
Да, я это сделал, и это работает. Но я видел и другие более сложные реализации, такие как stackoverflow.com/questions/1252468/…
vikky.rk
1
@Doorknob et. и др. У него отсутствует кодировка, и его пример (после исправления синтаксиса) будет работать, но его метод все еще неверен.
Gus

Ответы:

83

РЕДАКТИРОВАТЬ (2018): отредактированный ответ брата от @xinyongCheng - более простой подход, и он должен быть принятым ответом.

Ваш подход был бы разумным, если бы вы знали, что байты находятся в кодировке платформы по умолчанию. В вашем примере это правда, потому что k.getBytes()возвращает байты в кодировке платформы по умолчанию.

Чаще вы захотите указать кодировку. Однако есть более простой способ сделать это, чем заданный вами вопрос. String API предоставляет методы, которые преобразуют массив String в массив byte [] в определенной кодировке. Эти методы предлагают использовать CharsetEncoder / CharsetDecoder, «когда требуется больший контроль над процессом декодирования [кодирования]».

Чтобы получить байты из String в определенной кодировке, вы можете использовать родственный метод getBytes ():

byte[] bytes = k.getBytes( StandardCharsets.UTF_8 );

Чтобы поместить байты с определенной кодировкой в ​​String, вы можете использовать другой конструктор String:

String v = new String( bytes, StandardCharsets.UTF_8 );

Обратите внимание, что ByteBuffer.array()это необязательная операция. Если вы создали свой ByteBuffer с массивом, вы можете использовать этот массив напрямую. В противном случае, если вы хотите быть в безопасности, используйте ByteBuffer.get(byte[] dst, int offset, int length)для получения байтов из буфера в массив байтов.

Энди Томас
источник
а в ByteBuffer.getфункции ввод снова представляет собой массив байтов, как мне его получить? нет смысла повторять k.getbytes, не так ли?
Уильям Кинаан,
@WilliamKinaan - У вас есть байт [], на который вы кормили ByteBuffer.get(byte[] dst, int offset, int length). Вы можете построить из него String с помощью конструктора String () `String (byte [] bytes, int offset, int length, Charset charset). Вы можете использовать одинаковые значения смещения и длины для обоих вызовов.
Энди Томас
В java.nio.ByteBuffer нет метода k.getBytes () (может быть, не в той версии, которую я использую). Поэтому я использовал метод k.array (), который вернет byte [].
Мадура Прадип
@MaduraPradeep - В примере кода в вопросе и в этом ответе kэто String, а не ByteBuffer.
Энди Томас
Имейте в виду, что UTF-8 может быть не оптимальной кодировкой для преобразования байтов в строки и наоборот. Для отображения в 1-к-1 байт для символов лучше использовать ISO-8859-1, см stackoverflow.com/questions/9098022/...
asmaier
103

Энди Томас упомянул более простой способ без проблем декодировать a ByteBufferв a String.

String s = StandardCharsets.UTF_8.decode(byteBuffer).toString();
Синьюн Чэн
источник
2
Имейте в виду, что UTF-8 может быть не оптимальной кодировкой для преобразования байтов в строки и наоборот. Для сопоставления байтов с символами «1 к 1» лучше использовать ISO-8859-1, см. Stackoverflow.com/questions/9098022/… .
asmaier
Кроме того , из не очень нужна строка, то CharBuffer decode()возвращается это CharSequence(как String), так что вы можете избежать дополнительную копию и использовать его непосредственно.
Дэвид Эрманн
15

Попробуй это:

new String(bytebuffer.array(), "ASCII");

NB. вы не можете правильно преобразовать байтовый массив в String, не зная его кодировки.

надеюсь, это поможет

Дэн Брэй
источник
10
UTF-8, вероятно, лучший вариант по умолчанию, чем ASCII?
Gus
3
Ни то, ни другое не следует указывать, поскольку OP использует k.getBytes (), который использует кодировку платформы по умолчанию.
Энди Томас
7
Не все буферы поддерживаются массивом, поэтому .array()может возникнуть исключение.
Дмитрий Лазерка
Не все байтовые буферы поддерживают этот .array()метод.
ScalaWilliam
3
Осторожный! Если вы используете array(), вы также должны использовать, arrayOffset()чтобы начать с правильной позиции в массиве! Это небольшая ловушка, потому что обычно arrayOffset () имеет значение 0; но в тех редких случаях, когда это не так, вы столкнетесь с труднодоступными ошибками, если не примете их во внимание.
oliver
13

Просто хотел отметить, что предполагать, что ByteBuffer.array () всегда будет работать, небезопасно.

byte[] bytes;
if(buffer.hasArray()) {
    bytes = buffer.array();
} else {
    bytes = new byte[buffer.remaining()];
    buffer.get(bytes);
}
String v = new String(bytes, charset);

Обычно buffer.hasArray () всегда будет иметь значение true или false в зависимости от вашего варианта использования. На практике, если вы действительно не хотите, чтобы он работал ни при каких обстоятельствах, можно безопасно оптимизировать ветку, которая вам не нужна. Но остальные ответы могут не работать с ByteBuffer, созданным с помощью ByteBuffer.allocateDirect ().

Fuwjax
источник
Если буфер создается через ByteBuffer.wrap(bytes, offset, size)factory, .array()будет возвращен весь bytesмассив. Лучше используйте форму, которую предложил Синьюн Ченг
Лев Кузнецов
.Decode () в Charset - лучшее решение, согласен. Я действительно чувствую, что контекст моего ответа является полезной информацией, но сейчас гораздо меньше.
Fuwjax
2
Осторожный! Если вы используете array(), вы также должны использовать, arrayOffset()чтобы начать с правильной позиции в массиве! Это небольшая ловушка, потому что обычно arrayOffset () имеет значение 0; но в тех редких случаях, когда это не так, вы столкнетесь с труднодоступными ошибками, если не примете их во внимание.
oliver
8

Ответы, относящиеся к простому вызову array(), не совсем верны: когда буфер был частично использован или относится к части массива (вы можете ByteBuffer.wrapмассив с заданным смещением, не обязательно с самого начала), мы должны учитывать это в наших расчетах. Это общее решение, которое работает для буферов во всех случаях (не распространяется на кодировку):

if (myByteBuffer.hasArray()) {
    return new String(myByteBuffer.array(),
        myByteBuffer.arrayOffset() + myByteBuffer.position(),
        myByteBuffer.remaining());
} else {
    final byte[] b = new byte[myByteBuffer.remaining()];
    myByteBuffer.duplicate().get(b);
    return new String(b);
}

О проблемах, связанных с кодированием, см. Ответ Энди Томаса.

Алекс Ярмула
источник
2

Корень этого вопроса в том, как декодировать байты в строку?

это можно сделать с помощью JAVA NIO CharSet:

public final CharBuffer decode(ByteBuffer bb)

FileChannel channel = FileChannel.open(
  Paths.get("files/text-latin1.txt", StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);

CharSet latin1 = StandardCharsets.ISO_8859_1;
CharBuffer latin1Buffer = latin1.decode(buffer);

String result = new String(latin1Buffer.array());
  • Сначала создаем канал и читаем его в буфер
  • Затем метод decode декодирует буфер Latin1 в буфер символов
  • Затем мы можем поместить результат, например, в строку
宏杰 李
источник
Ваш код не декодируется с latin1 на utf8. Хотя ваш код правильный, вызов CharBuffer utf8Buffer несколько вводит в заблуждение, поскольку он не имеет кодировки.
Бьорн Линдквист
1

Обратите внимание (помимо проблемы с кодировкой), что для некоторых из более сложных связанных кодов возникают проблемы с получением «активной» части рассматриваемого ByteBuffer (например, с помощью позиции и ограничения), а не просто кодирования всех байтов. во всем массиве поддержки (как и во многих примерах в этих ответах).

Jas
источник
1

Преобразуйте String в ByteBuffer, затем из ByteBuffer обратно в String с помощью Java:

import java.nio.charset.Charset;
import java.nio.*;

String babel = "obufscate thdé alphebat and yolo!!";
System.out.println(babel);
//Convert string to ByteBuffer:
ByteBuffer babb = Charset.forName("UTF-8").encode(babel);
try{
    //Convert ByteBuffer to String
    System.out.println(new String(babb.array(), "UTF-8"));
}
catch(Exception e){
    e.printStackTrace();
}

Что сначала печатает напечатанную пустую строку, а затем ByteBuffer, преобразованный в array ():

obufscate thdé alphebat and yolo!!
obufscate thdé alphebat and yolo!!

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

String text = "こんにちは";
//convert utf8 text to a byte array
byte[] array = text.getBytes("UTF-8");
//convert the byte array back to a string as UTF-8
String s = new String(array, Charset.forName("UTF-8"));
System.out.println(s);
//forcing strings encoded as UTF-8 as an incorrect encoding like
//say ISO-8859-1 causes strange and undefined behavior
String sISO = new String(array, Charset.forName("ISO-8859-1"));
System.out.println(sISO);

Печатает вашу строку, интерпретируемую как UTF-8, а затем снова как ISO-8859-1:

こんにちは
ããã«ã¡ã¯
Эрик Лещинский
источник
0
private String convertFrom(String lines, String from, String to) {
    ByteBuffer bb = ByteBuffer.wrap(lines.getBytes());
    CharBuffer cb = Charset.forName(to).decode(bb);
    return new String(Charset.forName(from).encode(cb).array());
};
public Doit(){
    String concatenatedLines = convertFrom(concatenatedLines, "CP1252", "UTF-8");
};
Koenraad Appelo
источник
0

Вот простая функция для преобразования байтового буфера в строку:

public String byteBufferToString(ByteBuffer bufferData) {
    byte[] buffer = new byte[bufferData.readableByteCount()];
    // read bufferData and insert into buffer 
    data.read(buffer);
    // CharsetUtil supports UTF_16, ASCII, and many more
    String text = new String(buffer, CharsetUtil.UTF_8);
    System.out.println("Text: "+text);
    return text;
}
Джитендра Асава
источник