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: Перетворення рядка в і з ByteBuffer і пов'язані з цим проблеми виглядають складнішими.


3
Ну, ти спробував?
tckmn

6
Так, я зробив, і це працює. Але я бачив інші реалізації , які є більш складними, як stackoverflow.com/questions/1252468 / ...
vikky.rk

1
@Doorknob та ін. ін. У нього відсутнє кодування, і його приклад (коли синтаксис буде виправлено) буде працювати, але його метод все ще не правильний.
Гас

Відповіді:


83

EDIT (2018): Відредагована відповідь на побратим @xinyongCheng - це простіший підхід, і його слід прийняти.

Ваш підхід був би розумним, якби ви знали, що байти знаходяться в таблиці за замовчуванням платформи. У вашому прикладі це правда, тому що k.getBytes()повертає байти в схему за замовчуванням платформи.

Частіше вам потрібно вказати кодування. Однак є простіший спосіб зробити це, ніж питання, яке ви пов’язали. API String надає методи, які перетворюють між масивом String і байтом [] в конкретне кодування. Ці методи пропонують використовувати CharsetEncoder / CharsetDecoder, "коли потрібен більше контроль над процесом декодування [кодування]".

Щоб отримати байти з рядка в певному кодуванні, ви можете скористатися методом 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 (байт [] байтів, int зміщення, int довжина, charset charset). Для обох дзвінків можна використовувати однакові значення зміщення та тривалості.
Енді Томас

У java.nio.ByteBuffer (можливо, у версії, яку я використовую) немає методу k.getBytes (). Тому я використав метод k.array (), який поверне байт [].
Мадура Прадіп

@MaduraPradeep - у прикладі коду у запитанні та у цій відповіді kє рядок, а не ByteBuffer.
Енді Томас

Пам’ятайте, що UTF-8 може бути не оптимальною схемою для перетворення байтів у рядки та навпаки. Для відображення в 1-к-1 байт для символів краще використовувати ISO-8859-1, см stackoverflow.com/questions/9098022 / ...
asmaier

103

Існує більш простий підхід до розшифровки a ByteBufferв Stringбез проблем, про який згадував Енді Томас.

String s = StandardCharsets.UTF_8.decode(byteBuffer).toString();

2
Пам’ятайте, що UTF-8 може бути не оптимальною схемою для перетворення байтів у рядки та навпаки. Для відображення байтів на символи 1 на 1 краще використовувати ISO-8859-1, див. Stackoverflow.com/questions/9098022/… .
Асмаєр

Крім того, з вас дійсно не потрібна рядок, CharBuffer decode()повернення - це CharSequence(як String), тому ви можете уникнути зайвої копії та використовувати її безпосередньо.
Девід Ерман

15

Спробуйте це:

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

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

Я сподіваюся, що це допомагає


10
UTF-8, мабуть, краща здогадка за замовчуванням, ніж ASCII?
Гас

3
Це не повинно бути вказано, враховуючи, що ОП використовує k.getBytes (), який використовує схему за замовчуванням платформи.
Енді Томас

7
Не всі буфери підтримуються масивом, тому .array()можуть бути винятком.
Дмитро Лазерка

Не всі байтбуфери підтримують .array()метод.
ScalaWilliam

3
Обережно! Якщо ви використовуєте array(), ви також повинні використовувати arrayOffset()для запуску з правильного положення в масиві! Це тонкий підводний камінь, оскільки зазвичай arrayOffset () дорівнює 0; але в тих рідкісних випадках, коли це не так, ви отримаєте важко знайти помилки, якщо не врахуєте їх.
Олівер

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 () завжди буде істинним або хибним, залежно від випадку використання. На практиці, якщо ви дійсно не хочете, щоб він працював за будь-яких обставин, можна безпечно оптимізувати відділення, яке вам не потрібно. Але решта відповідей може не працювати з ByteBuffer, створеним за допомогою ByteBuffer.allocateDirect ().


Якщо буфер створений через ByteBuffer.wrap(bytes, offset, size)завод .array(), поверне весь bytesмасив. Краще скористайтеся формою xinyong Cheng, запропонованою
Лев Кузнецов,

.Decode () на Charset - це краще рішення, погоджене. Я відчуваю, що контекст моєї відповіді є корисною інформацією, але набагато менше, ніж зараз.
Fuwjax

2
Обережно! Якщо ви використовуєте array(), ви також повинні використовувати arrayOffset()для запуску з правильного положення в масиві! Це тонкий підводний камінь, оскільки зазвичай arrayOffset () дорівнює 0; але в тих рідкісних випадках, коли це не так, ви отримаєте важко знайти помилки, якщо не врахуєте їх.
олівер

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());
  • Спочатку ми створюємо канал і читаємо його в буфері
  • Потім метод декодування декодує буфер Latin1 до буфера char
  • Потім ми можемо помістити результат, наприклад, у рядку

Ваш код не розшифровується з latin1 до utf8. Хоча ваш код правильний, виклик CharBuffer utf8Buffer дещо вводить в оману, оскільки він не має кодування.
Бьорн Ліндквіст

1

Зауважте (окрім проблеми кодування), що деякі складніші коди, пов'язані з більш складним кодом, пов'язані з проблемою отримання "активної" частини питання ByteBuffer (наприклад, використовуючи позицію та ліміт), а не просто кодуючи всі байти у всьому резервному масиві (як і багато прикладів у цих відповідях).


1

Перетворіть рядок у 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, призначений для масиву ():

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