Java ByteBuffer en chaîne

122

Est-ce une approche correcte pour convertir ByteBuffer en String de cette manière,

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 raison pour laquelle je pose la question est que cela semble trop simple, alors que d'autres approches comme Java: convertir une chaîne vers et depuis ByteBuffer et les problèmes associés semblent plus complexes.

vikky.rk
la source
3
Eh bien, avez-vous essayé?
tckmn
6
Oui je l'ai fait et ça marche. Mais j'ai vu d'autres implémentations qui sont plus complexes, comme stackoverflow.com/questions/1252468/…
vikky.rk
1
@Doorknob et. Al. Il manque l'encodage et son exemple (lorsque la syntaxe est corrigée) fonctionnera, mais sa méthode n'est toujours pas correcte.
Gus

Réponses:

83

EDIT (2018): La réponse des frères et sœurs éditée par @xinyongCheng est une approche plus simple et devrait être la réponse acceptée.

Votre approche serait raisonnable si vous saviez que les octets sont dans le jeu de caractères par défaut de la plate-forme. Dans votre exemple, cela est vrai car k.getBytes()renvoie les octets du jeu de caractères par défaut de la plateforme.

Plus fréquemment, vous voudrez spécifier l'encodage. Cependant, il existe un moyen plus simple de le faire que la question que vous avez liée. L'API String fournit des méthodes qui convertissent entre un tableau String et un tableau d'octets [] dans un codage particulier. Ces méthodes suggèrent d'utiliser CharsetEncoder / CharsetDecoder "lorsqu'un contrôle accru sur le processus de décodage [d'encodage] est nécessaire".

Pour obtenir les octets d'une chaîne dans un encodage particulier, vous pouvez utiliser une méthode getBytes () sœur:

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

Pour placer des octets avec un encodage particulier dans une chaîne, vous pouvez utiliser un constructeur de chaîne différent:

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

Notez qu'il ByteBuffer.array()s'agit d'une opération facultative. Si vous avez construit votre ByteBuffer avec un tableau, vous pouvez utiliser ce tableau directement. Sinon, si vous voulez être sûr, utilisez ByteBuffer.get(byte[] dst, int offset, int length)pour obtenir des octets du tampon dans un tableau d'octets.

Andy Thomas
la source
et dans la ByteBuffer.getfonction, l'entrée est à nouveau un tableau d'octets, comment puis-je l'obtenir? cela n'a aucun sens de redire k.getbytes, n'est-ce pas?
William Kinaan
@WilliamKinaan - Vous avez l'octet [] auquel vous avez alimenté ByteBuffer.get(byte[] dst, int offset, int length). Vous pouvez en construire une String avec le constructeur String () `String (byte [] bytes, int offset, int length, Charset charset). Vous pouvez utiliser les mêmes valeurs de décalage et de longueur pour les deux appels.
Andy Thomas
Il n'y a pas de méthode k.getBytes () dans java.nio.ByteBuffer (peut ne pas être dans la version que j'utilise). J'ai donc utilisé la méthode k.array () qui retournera byte [].
Madura Pradeep
@MaduraPradeep - Dans l'exemple de code de la question et de cette réponse, kest une chaîne, pas un ByteBuffer.
Andy Thomas
Sachez que UTF-8 n'est peut-être pas le jeu de caractères optimal pour la conversion d'octets en chaînes et vice-versa. Pour un mappage 1-à-1 des octets en caractères, utilisez mieux ISO-8859-1, voir stackoverflow.com/questions/9098022/…
asmaier
103

Il existe une approche plus simple pour décoder un ByteBufferen un Stringsans aucun problème, mentionné par Andy Thomas.

String s = StandardCharsets.UTF_8.decode(byteBuffer).toString();
xinyong Cheng
la source
2
Sachez que UTF-8 n'est peut-être pas le jeu de caractères optimal pour la conversion d'octets en chaînes et vice-versa. Pour un mappage 1 à 1 d'octets en caractères, utilisez mieux ISO-8859-1, voir stackoverflow.com/questions/9098022/… .
asmaier
De plus, si vous n'avez pas vraiment besoin d'une chaîne, le CharBuffer decode()retour est un CharSequence(comme String), vous pouvez donc éviter une copie supplémentaire et l'utiliser directement.
David Ehrmann
15

Essaye ça:

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

NB. vous ne pouvez pas convertir correctement un tableau d'octets en chaîne sans connaître son codage.

J'espère que ça aide

Dan Bray
la source
10
UTF-8 est probablement une meilleure estimation par défaut que ASCII?
Gus
3
Aucun des deux ne doit être spécifié, étant donné l'utilisation par l'OP de k.getBytes (), qui utilise le jeu de caractères par défaut de la plateforme.
Andy Thomas
7
Tous les tampons ne sont pas sauvegardés par un tableau, ils .array()peuvent donc lever une exception.
Dzmitry Lazerka
Tous les bytebuffers ne prennent pas en charge la .array()méthode.
ScalaWilliam
3
Prudent! Si vous utilisez array(), vous devez également utiliser arrayOffset()pour démarrer à la bonne position dans le tableau! C'est un piège subtil, car généralement arrayOffset () vaut 0; mais dans les rares cas où ce n'est pas le cas, vous aurez des bogues difficiles à trouver si vous ne le prenez pas en compte.
oliver
13

Je voulais juste souligner qu'il n'est pas sûr de supposer que ByteBuffer.array () fonctionnera toujours.

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

En général, buffer.hasArray () sera toujours vrai ou faux selon votre cas d'utilisation. En pratique, à moins que vous ne souhaitiez vraiment que cela fonctionne en toutes circonstances, il est prudent d'optimiser la branche dont vous n'avez pas besoin. Mais le reste des réponses peut ne pas fonctionner avec un ByteBuffer qui a été créé via ByteBuffer.allocateDirect ().

Fuwjax
la source
Si le tampon est créé via l' ByteBuffer.wrap(bytes, offset, size)usine .array(), le bytestableau entier sera renvoyé . Mieux vaut utiliser la forme suggérée par xinyong Cheng
Lev Kuznetsov
Le .decode () sur Charset est une meilleure solution, d'accord. Je pense que le contexte de ma réponse est une information utile, mais beaucoup moins maintenant.
Fuwjax
2
Prudent! Si vous utilisez array(), vous devez également utiliser arrayOffset()pour démarrer à la bonne position dans le tableau! C'est un piège subtil, car généralement arrayOffset () vaut 0; mais dans les rares cas où ce n'est pas le cas, vous aurez des bogues difficiles à trouver si vous ne le prenez pas en compte.
oliver
8

Les réponses faisant référence à un simple appel array()ne sont pas tout à fait correctes: lorsque le tampon a été partiellement consommé, ou fait référence à une partie d'un tableau (vous pouvez ByteBuffer.wrapun tableau à un offset donné, pas forcément depuis le début), il faut tenir compte de cela dans nos calculs. C'est la solution générale qui fonctionne pour les tampons dans tous les cas (ne couvre pas l'encodage):

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

Pour les préoccupations liées à l'encodage, voir la réponse d'Andy Thomas.

Alex Yarmula
la source
2

la racine de cette question est de savoir comment décoder des octets en chaîne?

cela peut être fait avec le 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());
  • Nous créons d'abord un canal et le lisons dans un tampon
  • Ensuite, la méthode de décodage décode un tampon Latin1 en un tampon de caractères
  • Nous pouvons ensuite mettre le résultat, par exemple, dans une chaîne
宏杰 李
la source
Votre code ne décode pas de latin1 à utf8. Bien que votre code soit correct, appeler le CharBuffer utf8Buffer est quelque peu trompeur car il n'a pas d'encodage.
Björn Lindqvist
1

Notez (mis à part le problème de codage) que certains des codes les plus compliqués liés ont la difficulté d'obtenir la partie "active" du ByteBuffer en question (par exemple en utilisant la position et la limite), plutôt que de simplement coder tous les octets dans tout le tableau de support (comme le font de nombreux exemples de ces réponses).

Jas
la source
1

Convertissez une chaîne en ByteBuffer, puis de ByteBuffer en String en utilisant 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();
}

Qui imprime d'abord la chaîne nue imprimée, puis le ByteBuffer converti en array ():

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

Cela m'a également été utile, réduire la chaîne en octets primitifs peut aider à inspecter ce qui se passe:

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 votre chaîne interprétée comme UTF-8, puis à nouveau comme ISO-8859-1:

こんにちは
ããã«ã¡ã¯
Eric Leschinski
la source
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
la source
0

Voici une fonction simple pour convertir un tampon d'octets en chaîne:

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;
}
Jitendra Asawa
la source