Java ByteBuffer转为字符串


121

这是将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和ByteBuffer之间来回转换字符串以及相关的问题看起来就更加复杂。


3
好吧,您尝试过吗?
tckmn 2013年

6
是的,我做到了,而且有效。但我所看到的其他实现这是更复杂的,像stackoverflow.com/questions/1252468/...
vikky.rk

1
@Doorknob等。等 他缺少编码,他的示例(在纠正语法后)可以工作,但是他的方法仍然不正确。
Gus 2013年

Answers:


83

编辑(2018): @xinyongCheng编辑的同级答案是一种更简单的方法,应该被接受。

如果您知道字节在平台的默认字符集中,则您的方法将是合理的。在您的示例中,这是正确的,因为k.getBytes()返回平台默认字符集中的字节。

更常见的是,您需要指定编码。但是,有一种比链接的问题更简单的方法。String API提供了以特定编码在String和byte []数组之间转换的方法。这些方法建议“当需要对解码[encoding]过程进行更多控制时,使用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没有任何意义,不是吗?
William Kinaan

@WilliamKinaan-您已将字节[]喂给了ByteBuffer.get(byte[] dst, int offset, int length)。您可以使用String()构造函数`String(byte [] bytes,int offset,int length,Charset charset)在其中构建一个String。您可以为两个调用使用相同的偏移量和长度值。
安迪·托马斯

java.nio.ByteBuffer中没有k.getBytes()方法(可能不在所使用的版本中)。因此,我使用了k.array()方法,该方法将返回byte []。
Madura Pradeep

@MaduraPradeep-在问题和答案中的示例代码中,k是字符串,而不是ByteBuffer。
安迪·托马斯

请注意,UTF-8可能不是将字节转换为字符串(反之亦然)的最佳字符集。有关字节到字符的
一对一

102

安迪·托马斯(Andy Thomas)提到了一种ByteBuffer将a 解码为a String而没有任何问题的更简单方法。

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

2
请注意,UTF-8可能不是将字节转换为字符串(反之亦然)的最佳字符集。有关字节到字符的一对一映射,最好使用ISO-8859-1,请参见stackoverflow.com/questions/9098022/…
Asmaier

另外,在您实际上不需要字符串的情况下,CharBuffer decode()返回值是一个CharSequence(如String),因此您可以避免多余的副本并直接使用它。
David Ehrmann '18

15

试试这个:

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

注意 您无法在不知道其编码的情况下将字节数组正确转换为字符串。

我希望这有帮助


10
UTF-8可能比ASCII更好的默认猜测吗?
Gus 2013年

3
鉴于OP使用k.getBytes()(使用平台的默认字符集),因此都不应该指定两者。
安迪·托马斯

7
并非所有缓冲区都由数组支持,因此.array()可能会引发异常。
Dzmitry Lazerka '16

并非所有的字节缓冲区都支持该.array()方法。
ScalaWilliam's

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()始终为true或false。在实践中,除非您真的希望它在任何情况下都能工作,否则可以安全地优化不需要的分支。但是其余答案可能不适用于通过ByteBuffer.allocateDirect()创建的ByteBuffer。


如果缓冲区是通过ByteBuffer.wrap(bytes, offset, size)工厂创建的,.array()则将返回整个bytes数组。更好地利用形式王新勇程建议
列夫·库兹涅佐夫

同意,Charset上的.decode()是更好的解决方案。我确实认为我回答的内容是有用的信息,但现在要少得多。
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);
}

有关编码的问题,请参阅Andy Thomas的答案。


1

请注意(除了编码问题),链接的一些更复杂的代码会麻烦到获取所讨论的ByteBuffer的“活动”部分(例如,通过使用位置和限制),而不是简单地对所有字节进行编码在整个支持数组中(就像这些答案中的许多示例一样)。


1

使用Java将字符串转换为ByteBuffer,然后从ByteBuffer转换回String:

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

它先打印打印的裸字符串,然后打印转换为array()的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:

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

1

这个问题的根源是如何将字节解码为字符串?

这可以通过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会产生误导,因为它没有编码。
比昂·林德奎斯特(BjörnLindqvist)

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");
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.