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

1. Въведение

Често трябва да конвертираме между String и байт масив в Java. В този урок ще разгледаме подробно тези операции.

Първо ще разгледаме различни начини за преобразуване на String в байтов масив. След това ще разгледаме подобни операции в обратен ред.

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

A String се съхранява като масив от Unicode символи в Java. За да го преобразуваме в масив от байтове , ние превеждаме последователността от символи в последователност от байтове. За този превод използваме екземпляр на Charset . Този клас задава преобразуване между последователност от символи s и последователност от байтове s .

Ние посочваме горния процес като кодиране .

Можем да кодираме String в байтов масив в Java по няколко начина. Нека разгледаме всеки от тях подробно с примери.

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

Класът String предоставя три претоварени метода getBytes за кодиране на String в байтов масив :

  • getBytes () - кодира с помощта на стандартния набор от символи на платформата
  • getBytes (String charsetName) - кодира с помощта на посочения набор от символи
  • getBytes (Charset 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, ако посоченият набор от символи не се поддържа.

Поведението на горните две версии е недефинирано, ако входът съдържа символи, които не се поддържат от символа. За разлика от това, третата версия използва байтовия масив по подразбиране на charset за кодиране на неподдържан вход.

След това нека извикаме третата версия на метода 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 () , удобен метод, който кодира Unicode символи в байтове. Този метод винаги замества невалидни въведени и неприложими символи, използвайки масива от байтове за замяна на символа по подразбиране.

Нека използваме метода за кодиране , за да преобразуваме 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 - пуснете грешния вход
  • ЗАМЕНИТЕ - заменете грешния вход
  • ДОКЛАД - докладвайте за грешката, като върнете обект 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.