Преобразование строки в байтовый массив и обратное преобразование в Java

1. Введение

Нам часто требуется преобразование между String и байтовым массивом в Java. В этом руководстве мы подробно рассмотрим эти операции.

Сначала мы рассмотрим различные способы преобразования String в байтовый массив. Затем мы посмотрим на аналогичные операции в обратном порядке.

2. Преобразование строки в массив байтов

Строка хранится в виде массива символов Unicode в Java. Чтобы преобразовать его в байтовый массив, мы переводим последовательность символов в последовательность байтов. Для этого перевода мы используем экземпляр Charset . Этот класс определяет отображение между последовательностью символом s и последовательностью байт с .

Мы называем описанный выше процесс кодированием .

Мы можем закодировать строку в массив байтов в Java несколькими способами. Рассмотрим подробно каждый из них на примерах.

2.1. Использование String.getBytes ()

Класс String предоставляет три перегруженных метода getBytes для кодирования String в массив байтов :

  • getBytes () - кодирует с использованием кодировки платформы по умолчанию
  • getBytes (String charsetName) - кодирует с использованием именованной кодировки
  • getBytes (Charset charset) - кодирует с использованием предоставленной кодировки

Во-первых, давайте закодируем строку, используя кодировку платформы по умолчанию:

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

Вышеупомянутый метод зависит от платформы, поскольку он использует кодировку платформы по умолчанию. Мы можем получить эту кодировку, вызвав Charset.defaultCharset () .

Во-вторых, давайте закодируем строку с использованием именованной кодировки:

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

Этот метод вызывает исключение UnsupportedEncodingException, если указанная кодировка не поддерживается.

Поведение двух вышеуказанных версий не определено, если ввод содержит символы, не поддерживаемые кодировкой. Напротив, третья версия использует массив байтов замены по умолчанию для кодирования неподдерживаемого ввода.

Затем вызовем третью версию метода getBytes () и передадим экземпляр 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); }

Здесь мы используем фабричный метод Charset.forName для получения экземпляра Charset . Этот метод вызывает исключение времени выполнения, если имя запрошенной кодировки недопустимо. Он также вызывает исключение времени выполнения, если кодировка поддерживается в текущей JVM.

Однако некоторые наборы символов гарантированно будут доступны на каждой платформе Java. Класс StandardCharsets определяет константы для этих кодировок.

Наконец, давайте закодируем с использованием одной из стандартных кодировок:

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

На этом мы завершаем обзор различных версий getBytes . Затем давайте рассмотрим метод, предоставляемый самим Charset .

2.2. Использование Charset.encode ()

Класс Charset предоставляет encode () - удобный метод, кодирующий символы Юникода в байты. Этот метод всегда заменяет недопустимые входные и неотображаемые символы, используя массив байтов замены по умолчанию для кодировки.

Давайте воспользуемся методом encode для преобразования String в байтовый массив:

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

Как мы видим выше, неподдерживаемые символы были заменены байтом замены кодировки по умолчанию 63.

Подходы, использованные до сих пор, используют класс CharsetEncoder внутри для выполнения кодирования. Давайте рассмотрим этот класс в следующем разделе.

2.3. CharsetEncoder

CharsetEncoder преобразует символы Unicode в последовательность байтов для данной кодировки . Более того, он обеспечивает точный контроль над процессом кодирования .

Давайте воспользуемся этим классом для преобразования String в байтовый массив:

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

Здесь мы создаем экземпляр CharsetEncoder , вызывая метод newEncoder для объекта Charset .

Затем мы с указанием действия для условий ошибки путем вызова onMalformedInput () и onUnmappableCharacter () методы . Мы можем указать следующие действия:

  • IGNORE - сбросить ошибочный ввод
  • REPLACE - заменить ошибочный ввод
  • ОТЧЕТ - сообщить об ошибке, вернув объект CoderResult или выдав исключение CharacterCodingException.

Кроме того, мы используем метод replaceWith (), чтобы указать массив байтов замены .

На этом мы завершаем обзор различных подходов к преобразованию String в байтовый массив. Давайте теперь посмотрим на обратную операцию.

3. Преобразование байтового массива в строку

Речь идет о процессе преобразования байтов массива в строки , как декодирование . Подобно кодированию, для этого процесса требуется Charset .

Однако мы не можем просто использовать какую-либо кодировку для декодирования байтового массива. Мы должны использовать кодировку, которая использовалась для кодирования String в массив байтов .

Мы можем преобразовать байтовый массив в String разными способами. Разберем подробно каждую из них.

3.1. Использование конструктора строк

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

Все предыдущие подходы для внутреннего декодирования используют класс CharsetDecoder . Мы можем использовать этот класс напрямую для детального управления процессом декодирования :

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

Здесь мы заменяем недопустимые входные данные и неподдерживаемые символы на «?».

Если мы хотим получать информацию в случае неверных входных данных, мы можем изменить декодер следующим образом:

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

4. Вывод

В этой статье мы исследовали несколько способов преобразования String в байтовый массив и обратного преобразования . Мы должны выбрать подходящий метод на основе входных данных, а также уровня контроля, необходимого для недопустимых входных данных.

Как обычно, полный исходный код можно найти на GitHub.