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.


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.


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

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


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 ().


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.


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

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


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:

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

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");
};
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.