Java ByteBuffer 到字符串

Posted

技术标签:

【中文标题】Java ByteBuffer 到字符串【英文标题】:Java ByteBuffer to String 【发布时间】:2013-06-25 15:32:32 【问题描述】:

这样把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: Converting String to and from ByteBuffer and associated problems 这样的其他方法看起来更复杂。

【问题讨论】:

嗯,你试过了吗? 是的,我做到了,它有效。但我见过其他更复杂的实现,比如***.com/questions/1252468/… @Doorknob 等。人。他缺少编码,他的例子(当语法被纠正时)会起作用,但他的方法仍然不正确。 【参考方案1】:

Andy Thomas 提到,有一种更简单的方法可以毫无问题地将 ByteBuffer 解码为 String

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

【讨论】:

请注意,UTF-8 可能不是将字节转换为字符串的最佳字符集,反之亦然。对于字节到字符的一对一映射,最好使用 ISO-8859-1,请参阅***.com/questions/9098022/…。 另外,你们中的真的不需要字符串,CharBuffer decode() 返回的是CharSequence(如String),所以你可以避免额外的副本并直接使用它。 @DavidEhrmann CharBuffer 没有解码方法 - 你在这里指的是什么? @TomAnderson Charset 有一个 decode() 方法。 @DavidEhrmann 啊!我完全误解了你的评论,对不起!【参考方案2】:

EDIT (2018):编辑的兄弟answer by @xinyongCheng is a simpler approach, and should be the accepted answer。

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

更频繁地,您需要指定编码。但是,有一种比您链接的问题更简单的方法。 String API 提供了在特定编码中在 String 和 byte[] 数组之间进行转换的方法。这些方法建议使用 CharsetEncoder/CharsetDecoder“当需要对解码 [编码] 过程进行更多控制时。”

要从特定编码的字符串中获取字节,可以使用同级 getBytes() 方法:

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

要将具有特定编码的字节放入字符串,您可以使用不同的字符串构造函数:

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(byte[] bytes, int offset, int length, Charset charset) 从中构建一个 String。您可以对两个调用使用相同的偏移量和长度值。 java.nio.ByteBuffer 中没有 k.getBytes() 方法(可能不在我使用的版本中)。所以我使用了 k.array() 方法,它会返回 byte[]. @MaduraPradeep - 在问题和这个答案的示例代码中,k 是一个字符串,而不是一个字节缓冲区。 请注意,UTF-8 可能不是将字节转换为字符串的最佳字符集,反之亦然。对于字节到字符的一对一映射,最好使用 ISO-8859-1,请参阅***.com/questions/9098022/…【参考方案3】:

试试这个:

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

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

希望对你有帮助

【讨论】:

UTF-8 可能是比 ASCII 更好的默认猜测? 两者都不应该被指定,因为 OP 使用 k.getBytes(),它使用平台的默认字符集。 并非所有缓冲区都由数组支持,因此.array() 可能会抛出异常。 并非所有字节缓冲区都支持.array() 方法。 小心!如果您使用array(),您必须还使用arrayOffset() 在数组中的正确位置开始!这是一个微妙的陷阱,因为通常 arrayOffset() 为 0;但在极少数情况下,如果您不考虑它,您将获得难以发现的错误。【参考方案4】:

只是想指出,假设 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.allocateDirect() 创建的 ByteBuffer。

【讨论】:

如果缓冲区是通过ByteBuffer.wrap(bytes, offset, size)工厂创建的,.array()将返回整个bytes数组。最好使用程心勇建议的形式 Charset 上的 .decode() 是一个更好的解决方案,同意。我确实觉得我的答案的上下文是有用的信息,但现在更不用说了。 小心!如果您使用array(),您必须还使用arrayOffset() 在数组中的正确位置开始!这是一个微妙的陷阱,因为通常 arrayOffset() 为 0;但在极少数情况下,如果你不考虑它,你会遇到难以发现的错误。【参考方案5】:

简单调用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 的回答。

【讨论】:

【参考方案6】:

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

这可以通过 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 缓冲区解码为 char 缓冲区 然后我们可以将结果,例如,放在一个字符串中

【讨论】:

您的代码未从 latin1 解码为 utf8。虽然您的代码是正确的,但调用 CharBuffer utf8Buffer 有点误导,因为它没有编码。【参考方案7】:

使用 Java 将 String 转换为 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();

先打印打印出来的裸字符串,然后再将 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:

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

【讨论】:

【参考方案8】:

请注意(除了编码问题),一些更复杂的链接代码会导致获取相关 ByteBuffer 的“活动”部分的麻烦(例如通过使用位置和限制),而不是简单地编码所有整个后备数组中的字节数(正如这些答案中的许多示例所做的那样)。

【讨论】:

【参考方案9】:
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");
;

【讨论】:

【参考方案10】:

这是一个将字节缓冲区转换为字符串的简单函数:

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;

【讨论】:

【参考方案11】:

这是在 java.nio.ByteBuffer 实例上对我有用的唯一方法:

String fileContent = new String(bb.array(), StandardCharsets.UTF_8);

相关代码sn-p如下:

import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;


Path path = Paths.get("/home/binita/testbb");
FileChannel fileChannel = FileChannel.open(path, 
                 EnumSet.of(StandardOpenOption.READ
                    )
                 );  
            
ByteBuffer bb = ByteBuffer.allocate(1024);
int bytesRead = fileChannel.read(bb);
if(bytesRead > 0) 
 String fileContent = new String(bb.array(), StandardCharsets.UTF_8);

【讨论】:

以上是关于Java ByteBuffer 到字符串的主要内容,如果未能解决你的问题,请参考以下文章

Java ByteBuffer 到字符串

Netty中的字节操作

ByteBuffer 到 GWT 中的字符串

Netty 之缓冲区 ByteBuf 解读

在 Java 中将字符串转换为 ByteBuffer

在 Java 中将 ByteBuffer 转换为字符串