Java ByteBuffer thành chuỗi


121

Đây có phải là cách tiếp cận đúng để chuyển đổi ByteBuffer thành String theo cách này không,

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");

Lý do tôi hỏi là điều này trông quá đơn giản, trong khi các cách tiếp cận khác như Java: Chuyển đổi chuỗi sang và từ ByteBuffer và các vấn đề liên quan trông phức tạp hơn.


3
Chà, bạn đã thử chưa?
tckmn

6
Vâng, tôi đã làm và nó hoạt động. Nhưng tôi đã nhìn thấy hiện thực khác mà phức tạp hơn, như stackoverflow.com/questions/1252468/...
vikky.rk

1
@Doorknob et. al. Anh ấy bị thiếu mã hóa và ví dụ của anh ấy (khi cú pháp được sửa) sẽ hoạt động, nhưng phương pháp của anh ấy vẫn không đúng.
Gus

Câu trả lời:


83

EDIT (2018): Câu trả lời của anh chị em đã được chỉnh sửa bởi @xinyongCheng là một cách tiếp cận đơn giản hơn và phải là câu trả lời được chấp nhận.

Cách tiếp cận của bạn sẽ hợp lý nếu bạn biết các byte nằm trong bộ ký tự mặc định của nền tảng. Trong ví dụ của bạn, điều này đúng vì k.getBytes()trả về các byte trong bộ ký tự mặc định của nền tảng.

Thường xuyên hơn, bạn sẽ muốn chỉ định mã hóa. Tuy nhiên, có một cách đơn giản hơn để làm điều đó so với câu hỏi bạn đã liên kết. API chuỗi cung cấp các phương thức chuyển đổi giữa chuỗi và mảng [] byte trong một mã hóa cụ thể. Các phương pháp này đề xuất sử dụng CharsetEncoder / CharsetDecoder "khi cần kiểm soát nhiều hơn quá trình giải mã [mã hóa]."

Để lấy các byte từ một Chuỗi trong một mã hóa cụ thể, bạn có thể sử dụng phương thức getBytes () anh chị em:

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

Để đặt các byte với một mã hóa cụ thể vào một Chuỗi, bạn có thể sử dụng một phương thức khởi tạo Chuỗi khác:

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

Lưu ý rằng đó ByteBuffer.array()là một hoạt động tùy chọn. Nếu bạn đã tạo ByteBuffer của mình bằng một mảng, bạn có thể sử dụng trực tiếp mảng đó. Ngược lại, nếu bạn muốn an toàn, hãy sử dụng ByteBuffer.get(byte[] dst, int offset, int length)để lấy các byte từ bộ đệm vào một mảng byte.


và trong ByteBuffer.gethàm, đầu vào lại là một mảng byte, làm cách nào để lấy được? không có ý nghĩa gì khi nói lại k.getbytes, phải không?
William Kinaan,

@WilliamKinaan - Bạn có byte [] mà bạn đã cấp cho ByteBuffer.get(byte[] dst, int offset, int length). Bạn có thể xây dựng một chuỗi từ nó bằng phương thức khởi tạo String () `String (byte [] byte, int offset, int length, Charset charset). Bạn có thể sử dụng cùng một giá trị độ lệch và độ dài cho cả hai cuộc gọi.
Andy Thomas,

Không có phương thức k.getBytes () trong java.nio.ByteBuffer (có thể không có trong phiên bản tôi đang sử dụng). Vì vậy, tôi đã sử dụng phương thức k.array () sẽ trả về byte [].
Madura Pradeep

@MaduraPradeep - Trong mã ví dụ trong câu hỏi và câu trả lời này, klà một Chuỗi, không phải ByteBuffer.
Andy Thomas

Hãy lưu ý rằng UTF-8 có thể không phải là bộ ký tự tối ưu để chuyển đổi byte thành chuỗi và ngược lại. Để ánh xạ từ 1 đến 1 các byte thành các ký tự, hãy sử dụng ISO-8859-1 tốt hơn, hãy xem stackoverflow.com/questions/9098022/…
asmaier

102

Có một cách tiếp cận đơn giản hơn để giải mã a ByteBufferthành a Stringmà không gặp bất kỳ vấn đề nào, Andy Thomas đề cập.

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

2
Hãy lưu ý rằng UTF-8 có thể không phải là bộ ký tự tối ưu để chuyển đổi byte thành chuỗi và ngược lại. Để ánh xạ 1-1 giữa các byte thành các ký tự, hãy sử dụng ISO-8859-1 tốt hơn, hãy xem stackoverflow.com/questions/9098022/… .
asmaier

Ngoài ra, bạn không thực sự cần một chuỗi, các kết CharBuffer decode()quả trả về là một CharSequence(giống như String), vì vậy bạn có thể tránh một bản sao thừa và sử dụng nó trực tiếp.
David Ehrmann

15

Thử cái này:

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

NB. bạn không thể chuyển đổi một cách chính xác một mảng byte thành một Chuỗi mà không biết mã hóa của nó.

Tôi hi vọng cái này giúp được


10
UTF-8 có lẽ là một dự đoán mặc định tốt hơn ASCII?
Gus

3
Không nên chỉ định, với việc OP sử dụng k.getBytes (), sử dụng bộ ký tự mặc định của nền tảng.
Andy Thomas

7
Không phải tất cả các bộ đệm đều được hỗ trợ bởi một mảng, vì vậy .array()có thể đưa ra một ngoại lệ.
Dzmitry Lazerka

Không phải tất cả các bộ đệm byte đều hỗ trợ .array()phương pháp này.
ScalaWilliam

3
Cẩn thận! Nếu bạn sử dụng array(), bạn cũng phải sử dụng arrayOffset()để bắt đầu ở vị trí chính xác trong mảng! Đây là một khó khăn nhỏ, bởi vì thường arrayOffset () là 0; nhưng trong những trường hợp hiếm hoi không xảy ra, bạn sẽ gặp phải những lỗi khó tìm nếu không tính đến nó.
oliver

13

Tôi chỉ muốn chỉ ra rằng không an toàn khi cho rằng ByteBuffer.array () sẽ luôn hoạt động.

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

Thường thì buffer.hasArray () sẽ luôn đúng hoặc sai tùy thuộc vào trường hợp sử dụng của bạn. Trên thực tế, trừ khi bạn thực sự muốn nó hoạt động trong bất kỳ trường hợp nào, còn lại bạn nên tối ưu hóa nhánh mà bạn không cần. Nhưng phần còn lại của các câu trả lời có thể không hoạt động với ByteBuffer được tạo thông qua ByteBuffer.allocateDirect ().


Nếu bộ đệm được tạo thông qua ByteBuffer.wrap(bytes, offset, size)nhà máy .array()sẽ trả về toàn bộ bytesmảng. Tốt hơn hãy sử dụng biểu mẫu xinyong Cheng đề xuất
Lev Kuznetsov

.Decode () trên Charset là một giải pháp tốt hơn, đồng ý. Tôi cảm thấy bối cảnh câu trả lời của tôi là thông tin hữu ích, nhưng bây giờ ít hơn nhiều.
Fuwjax 24/02/17

2
Cẩn thận! Nếu bạn sử dụng array(), bạn cũng phải sử dụng arrayOffset()để bắt đầu ở vị trí chính xác trong mảng! Đây là một khó khăn nhỏ, bởi vì thường arrayOffset () là 0; nhưng trong những trường hợp hiếm hoi không xảy ra, bạn sẽ gặp phải những lỗi khó tìm nếu không tính đến nó.
oliver

8

Các câu trả lời đề cập đến việc gọi đơn giản array()là không hoàn toàn chính xác: khi bộ đệm đã được sử dụng một phần, hoặc đang tham chiếu đến một phần của mảng (bạn có thể ByteBuffer.wraplà một mảng tại một khoảng chênh lệch nhất định, không nhất thiết ngay từ đầu), chúng ta phải tính đến trong tính toán của chúng tôi. Đây là giải pháp chung hoạt động cho bộ đệm trong mọi trường hợp (không bao gồm mã hóa):

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);
}

Đối với các mối quan tâm liên quan đến mã hóa, hãy xem câu trả lời của Andy Thomas.


1

Lưu ý (ngoài vấn đề mã hóa) rằng một số mã phức tạp hơn được liên kết sẽ gây ra sự cố khi lấy phần "hoạt động" của ByteBuffer được đề cập (ví dụ bằng cách sử dụng vị trí và giới hạn), thay vì chỉ mã hóa tất cả các byte trong toàn bộ mảng hỗ trợ (như nhiều ví dụ trong các câu trả lời này).


1

Chuyển đổi một chuỗi thành ByteBuffer, sau đó từ ByteBuffer trở lại String bằng 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();
}

Đầu tiên sẽ in chuỗi trần đã in, và sau đó ByteBuffer được truyền tới array ():

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

Ngoài ra, điều này cũng hữu ích cho tôi, giảm chuỗi thành byte nguyên thủy có thể giúp kiểm tra những gì đang xảy ra:

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);

In chuỗi của bạn được hiểu là UTF-8, sau đó in lại dưới dạng ISO-8859-1:

こんにちは
ããã«ã¡ã¯

1

gốc của câu hỏi này là làm thế nào để giải mã byte thành chuỗi?

điều này có thể được thực hiện với 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());
  • Đầu tiên, chúng tôi tạo một kênh và đọc nó trong bộ đệm
  • Sau đó, phương pháp giải mã giải mã bộ đệm Latin1 thành bộ đệm char
  • Sau đó, chúng tôi có thể đặt kết quả, ví dụ, trong một chuỗi

Mã của bạn không giải mã từ latin1 đến utf8. Mặc dù mã của bạn là chính xác, nhưng việc gọi CharBuffer utf8Buffer hơi gây hiểu lầm vì nó không có mã hóa.
Björn Lindqvist

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");
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.