конвертировать строку в байтовый массив и инвертировать в Java

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

1. Вступление

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

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

2. ПреобразованиеString в массивByte

String хранится в Java как массив символов Юникода. Чтобы преобразовать его в массивbyte, мы переводим последовательность символов в последовательность байтов. Для этого переводаwe use an instance of Charset. This class specifies a mapping between a sequence of chars and a sequence of bytes.

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

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

2.1. ИспользуяString.getBytes()

The String class provides three overloaded getBytes methods to encode a String into a byte array:

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

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

  • getBytes (Charset charset) - кодируется с использованием предоставленной кодировки

Во-первых,let’s encode a string using the platform’s default charset:

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

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

Во-вторых,let’s encode a string using a named charset:

@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, если указанная кодировка не поддерживается.

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

Далееlet’s call the third version of the getBytes() method and pass an instance of 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 определяет константы для этих кодировок.

Наконец,let’s encode using one of the standard charsets:

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

The Charset class provides encode(), a convenient method that encodes Unicode characters into bytes. Этот метод всегда заменяет недопустимые входные и неотображаемые символы, используя массив байтов замены по умолчанию для кодировки.

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

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

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

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

2.3. CharsetEncoderс

CharsetEncoder transforms Unicode characters into a sequence of bytes for a given charset. Moreover, it provides fine-grained control over the encoding process.

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

@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 - отбросить ошибочный ввод

  • ЗАМЕНИТЬ - заменить ошибочный ввод

  • ОТЧЕТ - сообщить об ошибке, вернув объектCoderResult или бросивCharacterCodingException

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

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

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

We refer to the process of converting a byte array to a String as decoding. Подобно кодированию, для этого процесса требуетсяCharset.

Однако мы не можем просто использовать любой набор символов для декодирования байтового массива. We should use the charset that was used to encode the String into the byte array.

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

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

The String class has few constructors which take a byte array as input. Все они похожи на методgetBytes, но работают наоборот.

Во-первых,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);
}

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

По этой причине мы должны вообще избегать этого метода.

Во-вторых,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);
}

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

В-третьих,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);
}

Наконец,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);
}

Пока что мы преобразовали массивbyte вString с помощью конструктора. Давайте теперь рассмотрим другие подходы.

3.2. ИспользуяCharset.decode()

КлассCharset предоставляет методdecode(), который преобразуетByteBuffer в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);
}

Здесьthe invalid input is replaced with the default replacement character for the charset.

3.3. CharsetDecoderс

Все предыдущие подходы для внутреннего декодирования используют классCharsetDecoder. We can use this class directly for fine-grained control on the decoding process:

@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 как:

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

4. Заключение

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

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