Java ByteBuffer para String


121

Esta é uma abordagem correta para converter ByteBuffer em String desta forma,

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

A razão de eu perguntar é que isso parece muito simples, enquanto outras abordagens como Java: Converter String de e para ByteBuffer e problemas associados parece mais complexo.


3
Bem, você tentou?
tckmn

6
Sim, eu fiz e funciona. Mas eu vi outras implementações que são mais complexas, como stackoverflow.com/questions/1252468/…
vikky.rk

1
@Doorknob et. al. Ele está sem codificação e seu exemplo (quando a sintaxe é corrigida) funcionará, mas seu método ainda não está certo.
Gus

Respostas:


83

EDIT (2018): A resposta do irmão editada por @xinyongCheng é uma abordagem mais simples e deve ser a resposta aceita.

Sua abordagem seria razoável se você soubesse que os bytes estão no conjunto de caracteres padrão da plataforma. Em seu exemplo, isso é verdadeiro porque k.getBytes()retorna os bytes no conjunto de caracteres padrão da plataforma.

Mais frequentemente, você desejará especificar a codificação. No entanto, há uma maneira mais simples de fazer isso do que a pergunta que você vinculou. A API String fornece métodos que convertem entre uma String e uma matriz de bytes [] em uma codificação específica. Esses métodos sugerem o uso de CharsetEncoder / CharsetDecoder "quando mais controle sobre o processo de decodificação [codificação] é necessário".

Para obter os bytes de uma String em uma codificação específica, você pode usar um método irmão getBytes ():

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

Para colocar bytes com uma codificação específica em uma String, você pode usar um construtor String diferente:

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

Observe que ByteBuffer.array()é uma operação opcional. Se você construiu seu ByteBuffer com um array, pode usar esse array diretamente. Caso contrário, se você quiser estar seguro, use ByteBuffer.get(byte[] dst, int offset, int length)para obter bytes do buffer em uma matriz de bytes.


e na ByteBuffer.getfunção, a entrada é novamente um array de bytes, como posso obtê-lo? não faz sentido repetir k.getbytes, não é?
William Kinaan

@WilliamKinaan - Você tem o byte [] para o qual alimentou ByteBuffer.get(byte[] dst, int offset, int length). Você pode construir uma String a partir dele com o construtor String () `String (byte [] bytes, deslocamento interno, comprimento interno, conjunto de caracteres Charset). Você pode usar os mesmos valores de deslocamento e comprimento para ambas as chamadas.
Andy Thomas

Não há método k.getBytes () em java.nio.ByteBuffer (pode não estar na versão que estou usando). Então eu usei o método k.array () que retornará byte [].
Madura Pradeep

@MaduraPradeep - No código de exemplo na pergunta e nesta resposta, ké uma String, não um ByteBuffer.
Andy Thomas de

Esteja ciente de que UTF-8 pode não ser o conjunto de caracteres ideal para converter bytes em strings e vice-versa. Para um mapeamento 1-para-1 de bytes para caracteres, use melhor ISO-8859-1, consulte stackoverflow.com/questions/9098022/…
asmaier

102

Há uma abordagem mais simples para decodificar um ByteBufferem um Stringsem problemas, mencionada por Andy Thomas.

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

2
Esteja ciente de que UTF-8 pode não ser o conjunto de caracteres ideal para converter bytes em strings e vice-versa. Para um mapeamento 1 para 1 de bytes para caracteres, use melhor o ISO-8859-1, consulte stackoverflow.com/questions/9098022/… .
asmaier

Além disso, se você realmente não precisa de uma string, o CharBuffer decode()retorno é um CharSequence(como String), então você pode evitar uma cópia extra e usá-la diretamente.
David Ehrmann

15

Experimente isto:

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

NB. você não pode converter corretamente uma matriz de bytes em uma String sem saber sua codificação.

Eu espero que isso ajude


10
UTF-8 é provavelmente uma estimativa padrão melhor do que ASCII?
Gus

3
Nenhum deve ser especificado, dado o uso do OP de k.getBytes (), que usa o conjunto de caracteres padrão da plataforma.
Andy Thomas de

7
Nem todos os buffers são apoiados por uma matriz, portanto, .array()pode lançar uma exceção.
Dzmitry Lazerka

Nem todos os bytebuffers suportam o .array()método.
ScalaWilliam

3
Cuidado! Se você usar array(), você também deve usar arrayOffset()para começar na posição correta na matriz! Esta é uma armadilha sutil, porque geralmente arrayOffset () é 0; mas nos raros casos em que não é, você obterá bugs difíceis de encontrar se não levar isso em consideração.
oliver

13

Só queria ressaltar que não é seguro presumir que ByteBuffer.array () sempre funcionará.

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

Normalmente buffer.hasArray () sempre será verdadeiro ou falso, dependendo do seu caso de uso. Na prática, a menos que você realmente queira que funcione em qualquer circunstância, é seguro otimizar o branch de que você não precisa. Mas o resto das respostas podem não funcionar com um ByteBuffer criado por meio de ByteBuffer.allocateDirect ().


Se o buffer for criado via ByteBuffer.wrap(bytes, offset, size)fábrica .array()irá retornar todo o bytesarray. Melhor usar a forma sugerida por xinyong Cheng
Lev Kuznetsov

O .decode () no Charset é uma solução melhor, concordou. Sinto que o contexto da minha resposta é uma informação útil, mas muito menos agora.
Fuwjax

2
Cuidado! Se você usar array(), você também deve usar arrayOffset()para começar na posição correta na matriz! Esta é uma armadilha sutil, porque geralmente arrayOffset () é 0; mas nos raros casos em que não é, você obterá bugs difíceis de encontrar se não levar isso em consideração.
oliver

8

As respostas referentes a simplesmente chamar array()não são totalmente corretas: quando o buffer foi parcialmente consumido, ou está se referindo a uma parte de uma matriz (você pode ByteBuffer.wrapuma matriz em um determinado deslocamento, não necessariamente desde o início), temos que levar em conta isso em nossos cálculos. Esta é a solução geral que funciona para buffers em todos os casos (não cobre a codificação):

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

Para questões relacionadas à codificação, consulte a resposta de Andy Thomas.


1

Observe (além do problema de codificação) que alguns dos códigos mais complicados vinculados se dão ao trabalho de obter a parte "ativa" do ByteBuffer em questão (por exemplo, usando posição e limite), em vez de simplesmente codificar todos os bytes em toda a matriz de apoio (como muitos dos exemplos nessas respostas fazem).


1

Converta uma String em ByteBuffer e, em seguida, de ByteBuffer de volta em String usando 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();
}

Que imprime a string nua impressa primeiro e, em seguida, o ByteBuffer convertido em array ():

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

Isso também foi útil para mim, reduzir a string a bytes primitivos pode ajudar a inspecionar o que está acontecendo:

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

Imprime sua string interpretada como UTF-8 e, em seguida, novamente como ISO-8859-1:

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

1

a raiz desta questão é como decodificar bytes para string?

isso pode ser feito com o 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());
  • Primeiro criamos um canal e o lemos em um buffer
  • O método de decodificação decodifica um buffer Latin1 para um buffer char
  • Podemos então colocar o resultado, por exemplo, em uma String

Seu código não está decodificando de latin1 para utf8. Embora seu código esteja correto, chamar o CharBuffer utf8Buffer pode ser um tanto enganoso porque não tem codificação.
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");
};
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.