【问题标题】:Java ByteBuffer to StringJava ByteBuffer 到字符串
【发布时间】: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 这样的其他方法看起来更复杂。

【问题讨论】:

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

标签: java string serialization bytearray


【解决方案1】:

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

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

【讨论】:

  • 请注意,UTF-8 可能不是将字节转换为字符串的最佳字符集,反之亦然。对于字节到字符的一对一映射,最好使用 ISO-8859-1,请参阅stackoverflow.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,请参阅stackoverflow.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);
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-07-13
              • 1970-01-01
              • 1970-01-01
              • 2017-11-28
              相关资源
              最近更新 更多