Перейти к основному содержимому

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

· 7 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

1. Введение

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

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

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

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

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

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

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

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

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

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 для преобразования строки в массив байтов :

@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 преобразует символы Юникода в последовательность байтов для данного набора символов . Более того, он обеспечивает детальный контроль над процессом кодирования .

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

@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() для указания замещающего массива байтов .

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

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

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

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

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

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

Класс String имеет несколько конструкторов, принимающих на вход массив байтов . Все они похожи на метод getBytes , но работают наоборот.

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

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

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

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

Затем воспользуемся именованной кодировкой для декодирования:

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

Далее, давайте используем объект Charset для декодирования:

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

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

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

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

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

Здесь недопустимый ввод заменяется символом замены по умолчанию для набора символов.

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. Вывод

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

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