Java ByteBuffer na String

122

Czy to jest poprawne podejście do konwersji ByteBuffer na String w ten sposób,

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

Pytam o to, że wygląda to na zbyt proste, podczas gdy inne podejścia, takie jak Java: Konwersja ciągu do iz ByteBuffera i powiązane problemy wyglądają na bardziej złożone.

vikky.rk
źródło
3
Cóż, próbowałeś tego?
tckmn
6
Tak, zrobiłem i działa. Ale widziałem inne implementacje, które są bardziej złożone, takie jak stackoverflow.com/questions/1252468/ ...
vikky.rk
1
@Doorknob et. glin. Brakuje mu kodowania, a jego przykład (po poprawieniu składni) zadziała, ale jego metoda nadal nie jest poprawna.
Gus,

Odpowiedzi:

83

EDYCJA (2018): Edytowana odpowiedź rodzeństwa autorstwa @xinyongCheng jest prostszym podejściem i powinna być akceptowaną odpowiedzią.

Twoje podejście byłoby rozsądne, gdybyś wiedział, że bajty znajdują się w domyślnym zestawie znaków platformy. W twoim przykładzie jest to prawda, ponieważ k.getBytes()zwraca bajty w domyślnym zestawie znaków platformy.

Częściej będziesz chciał określić kodowanie. Jest jednak na to prostszy sposób niż pytanie, które łączysz. String API udostępnia metody, które konwertują między String a tablicą byte [] w określonym kodowaniu. Te metody sugerują użycie CharsetEncoder / CharsetDecoder „gdy wymagana jest większa kontrola nad procesem dekodowania [kodowania]”.

Aby uzyskać bajty z String w określonym kodowaniu, możesz użyć siostrzanej metody getBytes ():

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

Aby umieścić bajty z określonym kodowaniem w String, możesz użyć innego konstruktora String:

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

Zauważ, że ByteBuffer.array()jest to operacja opcjonalna. Jeśli zbudowałeś swój ByteBuffer z tablicą, możesz użyć tej tablicy bezpośrednio. W przeciwnym razie, jeśli chcesz być bezpieczny, użyj polecenia, ByteBuffer.get(byte[] dst, int offset, int length)aby pobrać bajty z bufora do tablicy bajtów.

Andy Thomas
źródło
aw ByteBuffer.getfunkcji dane wejściowe są ponownie tablicą bajtów, jak mogę to uzyskać? nie ma sensu powtarzać k.getbytes, prawda?
William Kinaan
@WilliamKinaan - Masz bajt [], którym się nakarmiłeś ByteBuffer.get(byte[] dst, int offset, int length). Możesz zbudować z niego String za pomocą konstruktora String () `String (byte [] bytes, int offset, int length, Charset charset). Możesz użyć tych samych wartości przesunięcia i długości dla obu wywołań.
Andy Thomas
W java.nio.ByteBuffer nie ma metody k.getBytes () (może nie być w używanej wersji). Użyłem więc metody k.array (), która zwróci bajt [].
Madura Pradeep
@MaduraPradeep - w przykładowym kodzie w pytaniu i tej odpowiedzi kjest String, a nie ByteBuffer.
Andy Thomas
Należy pamiętać, że UTF-8 może nie być optymalnym zestawem znaków do konwersji bajtów na łańcuchy i odwrotnie. Aby uzyskać mapowanie 1-do-1 bajtów na znaki, lepiej użyj ISO-8859-1, zobacz stackoverflow.com/questions/9098022/ ...
asmaier
103

Jest prostsze podejście do dekodowania a ByteBufferna a Stringbez żadnych problemów, o którym wspomniał Andy Thomas.

String s = StandardCharsets.UTF_8.decode(byteBuffer).toString();
xinyong Cheng
źródło
2
Należy pamiętać, że UTF-8 może nie być optymalnym zestawem znaków do konwersji bajtów na łańcuchy i odwrotnie. Aby uzyskać mapowanie 1 do 1 bajtów na znaki, lepiej użyj ISO-8859-1, zobacz stackoverflow.com/questions/9098022/… .
asmaier
Ponadto, jeśli tak naprawdę nie potrzebujesz łańcucha, CharBuffer decode()zwraca to CharSequence(lubię String), więc możesz uniknąć dodatkowej kopii i użyć jej bezpośrednio.
David Ehrmann
15

Spróbuj tego:

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

NB. nie możesz poprawnie przekonwertować tablicy bajtów na String bez znajomości jej kodowania.

mam nadzieję, że to pomoże

Dan Bray
źródło
10
UTF-8 to prawdopodobnie lepsze domyślne odgadnięcie niż ASCII?
Gus
3
Żadne z nich nie powinno być określone, biorąc pod uwagę użycie przez OP funkcji k.getBytes (), która używa domyślnego zestawu znaków platformy.
Andy Thomas
7
Nie wszystkie bufory są obsługiwane przez tablicę, więc .array()może zgłosić wyjątek.
Dzmitry Lazerka
Nie wszystkie bufory bajtów obsługują tę .array()metodę.
ScalaWilliam
3
Ostrożny! Jeśli używasz array(), musisz również użyć, arrayOffset()aby rozpocząć od właściwej pozycji w tablicy! Jest to subtelna pułapka, ponieważ zwykle arrayOffset () ma wartość 0; ale w tych rzadkich przypadkach, w których tak nie jest, pojawią się trudne do znalezienia błędy, jeśli nie weźmiesz ich pod uwagę.
oliver
13

Chciałem tylko podkreślić, że nie jest bezpiecznie zakładać, że ByteBuffer.array () będzie zawsze działać.

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

Zazwyczaj wartość buffer.hasArray () zawsze będzie miała wartość true lub false, w zależności od przypadku użycia. W praktyce, chyba że naprawdę chcesz, aby działało w żadnych okolicznościach, możesz bezpiecznie zoptymalizować gałąź, której nie potrzebujesz. Ale pozostałe odpowiedzi mogą nie działać z ByteBuffer, który został utworzony przez ByteBuffer.allocateDirect ().

Fuwjax
źródło
Jeśli bufor zostanie utworzony przez ByteBuffer.wrap(bytes, offset, size)fabrykę .array(), zwróci całą bytestablicę. Lepiej użyj formy sugerowanej przez xinyong Cheng
Lev Kuznetsov
Zgodzono się, że .decode () w Charset jest lepszym rozwiązaniem. Wydaje mi się, że kontekst mojej odpowiedzi jest użyteczną informacją, ale teraz znacznie mniej.
Fuwjax
2
Ostrożny! Jeśli używasz array(), musisz również użyć, arrayOffset()aby rozpocząć od właściwej pozycji w tablicy! Jest to subtelna pułapka, ponieważ zwykle arrayOffset () ma wartość 0; ale w tych rzadkich przypadkach, w których tak nie jest, pojawią się trudne do znalezienia błędy, jeśli nie weźmiesz ich pod uwagę.
oliver
8

Odpowiedzi odnoszące się do prostego wywołania array()nie są do końca poprawne: gdy bufor został częściowo zużyty lub odnosi się do części tablicy (można ByteBuffer.wraptablicę z podanym przesunięciem, niekoniecznie od początku), musimy się liczyć to w naszych obliczeniach. To jest ogólne rozwiązanie, które działa dla buforów we wszystkich przypadkach (nie obejmuje kodowania):

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

Jeśli chodzi o obawy związane z kodowaniem, zobacz odpowiedź Andy'ego Thomasa.

Alex Yarmula
źródło
2

źródłem tego pytania jest jak zdekodować bajty do łańcucha?

można to zrobić za pomocą zestawu znaków JAVA NIO:

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());
  • Najpierw tworzymy kanał i czytamy go w buforze
  • Następnie metoda dekodowania dekoduje bufor Latin1 do bufora char
  • Następnie możemy umieścić wynik, na przykład, w String
宏杰 李
źródło
Twój kod nie dekoduje z latin1 do utf8. Chociaż Twój kod jest poprawny, wywołanie CharBuffer utf8Buffer jest nieco mylące, ponieważ nie ma kodowania.
Björn Lindqvist
1

Zauważ (pomijając problem z kodowaniem), że niektóre bardziej skomplikowane powiązane kody powodują problemy z uzyskaniem „aktywnej” części danego ByteBuffer (na przykład przez użycie pozycji i limitu), zamiast po prostu zakodować wszystkie bajty w całej tablicy bazowej (jak wiele przykładów w tych odpowiedziach).

Jas
źródło
1

Przekonwertuj String na ByteBuffer, a następnie z ByteBuffer z powrotem na String używając 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();
}

Który najpierw drukuje wydrukowany czysty ciąg, a następnie ByteBuffer rzutowany na array ():

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

Było to również pomocne dla mnie, zredukowanie ciągu do pierwotnych bajtów może pomóc w sprawdzeniu, co się dzieje:

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

Wyświetla ciąg znaków interpretowany jako UTF-8, a następnie ponownie jako ISO-8859-1:

こんにちは
ããã«ã¡ã¯
Eric Leschinski
źródło
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
źródło