Konwertuj ciąg na tablicę bajtów i odwróć w Javie

1. Wstęp

Często musimy konwertować między ciągami znaków i tablicą bajtów w Javie. W tym samouczku szczegółowo przeanalizujemy te operacje.

Najpierw przyjrzymy się różnych sposobów, aby przekonwertować ciąg na bajt tablicy. Następnie przyjrzymy się podobnym operacjom w odwrotnej kolejności.

2. Konwersja String na Byte Array

String jest przechowywany w postaci tablicy znaków Unicode w Javie. Aby przekonwertować go na tablicę bajtów , tłumaczymy sekwencję znaków na sekwencję bajtów. Do tego tłumaczenia używamy instancji Charset . Klasa ta określa mapowanie pomiędzy sekwencją char s i sekwencja bajtów s .

Odnosimy się do powyższego procesu jako kodowania .

Możemy zakodować String w tablicy bajtów w Javie na wiele sposobów. Przyjrzyjmy się szczegółowo każdemu z nich z przykładami.

2.1. Korzystanie z String.getBytes ()

Ciąg klasa zawiera trzy przeciążone getBytes metody kodowania ciąg do bajtu tablicy :

  • getBytes () - koduje używając domyślnego zestawu znaków platformy
  • getBytes (String charsetName) - koduje przy użyciu nazwanego zestawu znaków
  • getBytes (Charset charset) - koduje przy użyciu dostarczonego zestawu znaków

Po pierwsze, zakodujmy ciąg przy użyciu domyślnego zestawu znaków platformy:

String inputString = "Hello World!"; byte[] byteArrray = inputString.getBytes();

Powyższa metoda jest zależna od platformy, ponieważ używa domyślnego zestawu znaków platformy. Możemy uzyskać ten zestaw znaków, wywołując Charset.defaultCharset () .

Po drugie, zakodujmy ciąg przy użyciu nazwanego zestawu znaków:

@Test public void whenGetBytesWithNamedCharset_thenOK() throws UnsupportedEncodingException { String inputString = "Hello World!"; String charsetName = "IBM01140"; byte[] byteArrray = inputString.getBytes("IBM01140"); assertArrayEquals( new byte[] { -56, -123, -109, -109, -106, 64, -26, -106, -103, -109, -124, 90 }, byteArrray); }

Ta metoda zgłasza UnsupportedEncodingException, jeśli nazwany zestaw znaków nie jest obsługiwany.

Zachowanie powyższych dwóch wersji jest niezdefiniowane, jeśli dane wejściowe zawierają znaki, które nie są obsługiwane przez zestaw znaków. Z kolei trzecia wersja używa domyślnej tablicy bajtów zastępczych zestawu znaków do kodowania nieobsługiwanych danych wejściowych.

Następnie wywołajmy trzecią wersję metody getBytes () i przekażmy instancję Charset:

@Test public void whenGetBytesWithCharset_thenOK() { String inputString = "Hello ਸੰਸਾਰ!"; Charset charset = Charset.forName("ASCII"); byte[] byteArrray = inputString.getBytes(charset); assertArrayEquals( new byte[] { 72, 101, 108, 108, 111, 32, 63, 63, 63, 63, 63, 33 }, byteArrray); }

Tutaj używamy fabrycznej metody Charset.forName, aby uzyskać instancję Charset . Ta metoda zgłasza wyjątek czasu wykonywania, jeśli nazwa żądanego zestawu znaków jest nieprawidłowa. Zgłasza również wyjątek w czasie wykonywania, jeśli zestaw znaków jest obsługiwany w bieżącej JVM.

Jednak gwarantujemy, że niektóre zestawy znaków będą dostępne na każdej platformie Java. StandardCharsets klasa definiuje stałe dla tych zestawów znaków.

Na koniec zakodujmy, używając jednego ze standardowych zestawów znaków:

@Test public void whenGetBytesWithStandardCharset_thenOK() { String inputString = "Hello World!"; Charset charset = StandardCharsets.UTF_16; byte[] byteArrray = inputString.getBytes(charset); assertArrayEquals( new byte[] { -2, -1, 0, 72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33 }, byteArrray); }

W ten sposób kończymy przegląd różnych wersji getBytes . Następnie przyjrzyjmy się metodzie dostarczonej przez sam Charset .

2.2. Korzystanie z Charset.encode ()

Kodowanie klasy zapewnia kodowanie () , wygodny sposób, który koduje znaki Unicode w bajtach. Ta metoda zawsze zastępuje nieprawidłowe dane wejściowe i znaki niemapowalne przy użyciu domyślnej tablicy bajtów zastępczych zestawu znaków.

Użyjmy metody encode , aby przekonwertować String na tablicę bajtów :

@Test public void whenEncodeWithCharset_thenOK() { String inputString = "Hello ਸੰਸਾਰ!"; Charset charset = StandardCharsets.US_ASCII; byte[] byteArrray = charset.encode(inputString).array(); assertArrayEquals( new byte[] { 72, 101, 108, 108, 111, 32, 63, 63, 63, 63, 63, 33 }, byteArrray); }

Jak widać powyżej, nieobsługiwane znaki zostały zastąpione domyślnym bajtem zastępczym 63 zestawu znaków .

Dotychczas stosowane podejścia używają klasy CharsetEncoder wewnętrznie do wykonywania kodowania. Przeanalizujmy tę klasę w następnej sekcji.

2.3. CharsetEncoder

CharsetEncoder przekształca znaki Unicode w sekwencję bajtów dla danego zestawu znaków . Ponadto zapewnia precyzyjną kontrolę nad procesem kodowania .

Użyjmy tej klasy, aby przekonwertować String na tablicę bajtów :

@Test public void whenUsingCharsetEncoder_thenOK() throws CharacterCodingException { String inputString = "Hello ਸੰਸਾਰ!"; CharsetEncoder encoder = StandardCharsets.US_ASCII.newEncoder(); encoder.onMalformedInput(CodingErrorAction.IGNORE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .replaceWith(new byte[] { 0 }); byte[] byteArrray = encoder.encode(CharBuffer.wrap(inputString)) .array(); assertArrayEquals( new byte[] { 72, 101, 108, 108, 111, 32, 0, 0, 0, 0, 0, 33 }, byteArrray); }

Here, we're creating an instance of CharsetEncoder by calling the newEncoder method on a Charset object.

Then, we are specifying actions for error conditions by calling the onMalformedInput() and onUnmappableCharacter() methods. We can specify the following actions:

  • IGNORE – drop the erroneous input
  • REPLACE – replace the erroneous input
  • REPORT – report the error by returning a CoderResult object or throwing a CharacterCodingException

Furthermore, we are using the replaceWith() method to specify the replacement byte array .

Thus, we complete the review of various approaches to convert a String to a byte array. Let's next look at the reverse operation.

3. Converting Byte Array to String

We refer to the process of converting a byte array to a String as decoding. Similar to encoding, this process requires a Charset.

However, we cannot just use any charset for decoding a byte array. We should use the charset that was used to encode the String into the byte array.

We can convert a byte array to a String in many ways. Let's examine each of them in detail.

3.1. Using the String Constructor

The String class has few constructors which take a byte array as input. They are all similar to the getBytes method but work in reverse.

First, let's convert a byte array to String using the platform's default charset:

@Test public void whenStringConstructorWithDefaultCharset_thenOK() { byte[] byteArrray = { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 }; String string = new String(byteArrray); assertNotNull(string); }

Note that we don't assert anything here about the contents of the decoded string. This is because it may decode to something different, depending on the platform's default charset.

For this reason, we should generally avoid this method.

Secondly, let's use a named charset for decoding:

@Test public void whenStringConstructorWithNamedCharset_thenOK() throws UnsupportedEncodingException { String charsetName = "IBM01140"; byte[] byteArrray = { -56, -123, -109, -109, -106, 64, -26, -106, -103, -109, -124, 90 }; String string = new String(byteArrray, charsetName); assertEquals("Hello World!", string); }

This method throws an exception if the named charset is not available on the JVM.

Thirdly, let's use a Charset object to do decoding:

@Test public void whenStringConstructorWithCharSet_thenOK() { Charset charset = Charset.forName("UTF-8"); byte[] byteArrray = { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 }; String string = new String(byteArrray, charset); assertEquals("Hello World!", string); }

Finally, let's use a standard Charset for the same:

@Test public void whenStringConstructorWithStandardCharSet_thenOK() { Charset charset = StandardCharsets.UTF_16; byte[] byteArrray = { -2, -1, 0, 72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33 }; String string = new String(byteArrray, charset); assertEquals("Hello World!", string); }

So far, we have converted a byte array into a String using the constructor. Let's now look into the other approaches.

3.2. Using Charset.decode()

The Charset class provides the decode() method that converts a ByteBuffer to String:

@Test public void whenDecodeWithCharset_thenOK() { byte[] byteArrray = { 72, 101, 108, 108, 111, 32, -10, 111, 114, 108, -63, 33 }; Charset charset = StandardCharsets.US_ASCII; String string = charset.decode(ByteBuffer.wrap(byteArrray)) .toString(); assertEquals("Hello �orl�!", string); }

Here, the invalid input is replaced with the default replacement character for the charset.

3.3. CharsetDecoder

Wszystkie poprzednie podejścia do wewnętrznego dekodowania używają klasy CharsetDecoder . Możemy użyć tej klasy bezpośrednio do precyzyjnej kontroli procesu dekodowania :

@Test public void whenUsingCharsetDecoder_thenOK() throws CharacterCodingException { byte[] byteArrray = { 72, 101, 108, 108, 111, 32, -10, 111, 114, 108, -63, 33 }; CharsetDecoder decoder = StandardCharsets.US_ASCII.newDecoder(); decoder.onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .replaceWith("?"); String string = decoder.decode(ByteBuffer.wrap(byteArrray)) .toString(); assertEquals("Hello ?orl?!", string); }

Tutaj zastępujemy nieprawidłowe dane wejściowe i nieobsługiwane znaki znakiem „?”.

Jeśli chcemy być informowani o nieprawidłowych wejściach, możemy zmienić dekoder jako:

decoder.onMalformedInput(CodingErrorAction.REPORT) .onUnmappableCharacter(CodingErrorAction.REPORT)

4. Wniosek

W tym artykule, zbadaliśmy wiele sposobów konwersji string do tablicy bajtów i odwrotnie. Powinniśmy wybrać odpowiednią metodę w oparciu o dane wejściowe, a także poziom kontroli wymagany w przypadku nieprawidłowych danych wejściowych.

Jak zwykle pełny kod źródłowy można znaleźć na GitHub.