ByteBuffer de Java a cadena

121

¿Es este un enfoque correcto para convertir ByteBuffer en String de esta manera?

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

La razón por la que pregunto es que esto parece demasiado simple, mientras que otros enfoques como Java: Convertir cadenas desde y hacia ByteBuffer y los problemas asociados parecen más complejos.

vikky.rk
fuente
3
Bueno, lo intentaste?
tckmn
6
Sí lo hice y funciona. Pero he visto otras implementaciones que son más complejas, como stackoverflow.com/questions/1252468/…
vikky.rk
1
@Pomo de la puerta et. Alabama. Le falta codificación y su ejemplo (cuando se corrige la sintaxis) funcionará, pero su método aún no es correcto.
Gus

Respuestas:

83

EDITAR (2018): La respuesta de hermanos editada por @xinyongCheng es un enfoque más simple y debería ser la respuesta aceptada.

Su enfoque sería razonable si supiera que los bytes están en el juego de caracteres predeterminado de la plataforma. En su ejemplo, esto es cierto porquek.getBytes() devuelve los bytes en el juego de caracteres predeterminado de la plataforma.

Con más frecuencia, querrá especificar la codificación. Sin embargo, hay una forma más sencilla de hacerlo que la pregunta que vinculó. La API de cadena proporciona métodos que convierten entre una cadena y una matriz de bytes [] en una codificación particular. Estos métodos sugieren usar CharsetEncoder / CharsetDecoder "cuando se requiere más control sobre el proceso de decodificación [codificación]".

Para obtener los bytes de una cadena en una codificación particular, puede usar un método hermano getBytes ():

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

Para poner bytes con una codificación particular en una cadena, puede usar un constructor de cadena diferente:

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

Tenga en cuenta que ByteBuffer.array()es una operación opcional. Si ha construido su ByteBuffer con una matriz, puede usar esa matriz directamente. De lo contrario, si desea estar seguro, use ByteBuffer.get(byte[] dst, int offset, int length)para obtener bytes del búfer en una matriz de bytes.

Andy Thomas
fuente
y en la ByteBuffer.getfunción, la entrada es nuevamente una matriz de bytes, ¿cómo puedo obtenerla? no tiene sentido volver a decir k.getbytes, ¿verdad?
William Kinaan
@WilliamKinaan - Tienes el byte [] que alimentaste ByteBuffer.get(byte[] dst, int offset, int length). Puede construir un String a partir de él con el constructor String () `String (byte [] bytes, int offset, int length, Charset charset). Puede utilizar los mismos valores de desplazamiento y longitud para ambas llamadas.
Andy Thomas
No hay un método k.getBytes () en java.nio.ByteBuffer (puede que no esté en la versión que estoy usando). Entonces utilicé el método k.array () que devolverá el byte [].
Madura Pradeep
@MaduraPradeep: en el código de ejemplo de la pregunta y esta respuesta, kes una cadena, no un ByteBuffer.
Andy Thomas
Tenga en cuenta que UTF-8 puede no ser el conjunto de caracteres óptimo para convertir bytes en cadenas y viceversa. Para una asignación 1 a 1 de bytes a caracteres, use mejor ISO-8859-1, consulte stackoverflow.com/questions/9098022/…
asmaier
102

Hay un enfoque más simple para decodificar un ByteBufferen un Stringsin ningún problema, mencionado por Andy Thomas.

String s = StandardCharsets.UTF_8.decode(byteBuffer).toString();
xinyong Cheng
fuente
2
Tenga en cuenta que UTF-8 puede no ser el conjunto de caracteres óptimo para convertir bytes en cadenas y viceversa. Para una asignación 1 a 1 de bytes a caracteres, use mejor ISO-8859-1, consulte stackoverflow.com/questions/9098022/… .
asmaier
Además, si realmente no necesita una cadena, los CharBuffer decode()retornos son un CharSequence(me gusta String), por lo que puede evitar una copia adicional y usarla directamente.
David Ehrmann
15

Prueba esto:

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

NÓTESE BIEN. no puede convertir correctamente una matriz de bytes en una cadena sin conocer su codificación.

espero que esto ayude

Dan Bray
fuente
10
¿UTF-8 es probablemente una suposición predeterminada mejor que ASCII?
Gus
3
Ninguno de los dos debe especificarse, dado que el OP usa k.getBytes (), que usa el juego de caracteres predeterminado de la plataforma.
Andy Thomas
7
No todos los búferes están respaldados por una matriz, por lo que .array()pueden generar una excepción.
Dzmitry Lazerka
No todos los búferes de bytes admiten el .array()método.
ScalaWilliam
3
¡Cuidado! Si lo usa array(), también debe usar arrayOffset()para comenzar en la posición correcta en la matriz. Este es un error sutil, porque normalmente arrayOffset () es 0; pero en los raros casos en los que no lo es, obtendrá errores difíciles de encontrar si no los tiene en cuenta.
oliver
13

Solo quería señalar que no es seguro asumir que ByteBuffer.array () siempre funcionará.

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

Por lo general, buffer.hasArray () siempre será verdadero o falso dependiendo de su caso de uso. En la práctica, a menos que realmente desee que funcione bajo cualquier circunstancia, es seguro optimizar la rama que no necesita. Pero es posible que el resto de las respuestas no funcionen con un ByteBuffer creado mediante ByteBuffer.allocateDirect ().

Fuwjax
fuente
Si el búfer se crea a través de ByteBuffer.wrap(bytes, offset, size)fábrica .array(), devolverá la bytesmatriz completa . Mejor use la forma que sugirió xinyong Cheng
Lev Kuznetsov
El .decode () en Charset es una mejor solución, estuvo de acuerdo. Siento que el contexto de mi respuesta es información útil, pero mucho menos ahora.
Fuwjax
2
¡Cuidado! Si lo usa array(), también debe usar arrayOffset()para comenzar en la posición correcta en la matriz. Este es un error sutil, porque normalmente arrayOffset () es 0; pero en los raros casos en los que no lo es, obtendrá errores difíciles de encontrar si no los tiene en cuenta.
oliver
8

Las respuestas que se refieren a simplemente llamar array()no son del todo correctas: cuando el búfer se ha consumido parcialmente o se refiere a una parte de una matriz (puede ByteBuffer.wrapuna matriz en un desplazamiento dado, no necesariamente desde el principio), tenemos que tener en cuenta eso en nuestros cálculos. Esta es la solución general que funciona para búferes en todos los casos (no cubre la codificación):

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 las preocupaciones relacionadas con la codificación, consulte la respuesta de Andy Thomas.

Alex Yarmula
fuente
1

Tenga en cuenta (aparte del problema de codificación) que algunos de los códigos más complicados vinculados se toman la molestia de obtener la parte "activa" del ByteBuffer en cuestión (por ejemplo, usando la posición y el límite), en lugar de simplemente codificar todos los bytes en toda la matriz de respaldo (como lo hacen muchos de los ejemplos en estas respuestas).

Jas
fuente
1

Convierta una cadena en ByteBuffer, luego de ByteBuffer de nuevo a 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 la cadena desnuda impresa primero, y luego el ByteBuffer convertido en array ():

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

También esto fue útil para mí, reducir la cadena a bytes primitivos puede ayudar a inspeccionar lo que está sucediendo:

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 su cadena interpretada como UTF-8, y luego nuevamente como ISO-8859-1:

こんにちは
ããã«ã¡ã¯
Eric Leschinski
fuente
1

la raíz de esta pregunta es ¿cómo decodificar bytes en cadenas?

esto se puede hacer con 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());
  • Primero creamos un canal y lo leemos en un búfer
  • Luego, el método de decodificación decodifica un búfer Latin1 en un búfer char
  • Luego podemos poner el resultado, por ejemplo, en una cadena
宏杰 李
fuente
Su código no se está decodificando de latin1 a utf8. Si bien su código es correcto, llamar al CharBuffer utf8Buffer es algo engañoso porque no tiene codificación.
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");
};
Koenraad Appelo
fuente