Як перетворити байтовий масив у рядок і навпаки?


248

Мені доводиться конвертувати байтовий масив в рядок в Android, але мій байтовий масив містить негативні значення.

Якщо я знову перетворять цей рядок у байтовий масив, отримані нами значення відрізняються від вихідних значень масиву байтів.

Що я можу зробити, щоб отримати належну конверсію? Код, який я використовую для перетворення, такий:

// Code to convert byte arr to str:
byte[] by_original = {0,1,-2,3,-4,-5,6};
String str1 = new String(by_original);
System.out.println("str1 >> "+str1);

// Code to convert str to byte arr:
byte[] by_new = str1.getBytes();
for(int i=0;i<by_new.length;i++) 
System.out.println("by1["+i+"] >> "+str1);

Я застряг у цій проблемі.


3
Чому ви намагаєтеся в першу чергу перетворити довільні двійкові дані в рядки? Окрім усіх проблем із діаграмою, з якими вже згадуються відповіді, є й той факт, що ви зловживаєте String, якщо це робите. Що не так у використанні byte[]для своїх бінарних даних та Stringтексту?
Йоахім Зауер

8
@Joachim - іноді у вас є зовнішні інструменти, які можуть робити такі речі, як магазинні рядки. У цьому випадку ви хочете мати змогу перетворити байтовий масив у (закодований певним чином) рядок.
Джеймс Мур

Відповіді:


377

У вашому байтовому масиві повинно бути деяке кодування. Кодування не може бути ASCII, якщо у вас є негативні значення. Після того, як ви зрозумієте, ви можете перетворити набір байтів у String, використовуючи:

byte[] bytes = {...}
String str = new String(bytes, "UTF-8"); // for UTF-8 encoding

Є купа кодувань, якими ви можете скористатися, подивіться на клас Charset у Sun javadocs .


4
@MauricePerry, чи можете ви пояснити, чому це не працюватиме UTF-8?
Асиф Муштак

12
@UnK вядомы тому, що UTF-8 кодує деякі символи у вигляді 2- або 3- байт-рядків. Не кожен байтний масив є дійсним рядком, закодованим UTF-8. ISO-8859-1 був би кращим вибором: тут кожен символ кодується як байт.
Моріс Перрі

1
Це може спрацювати, але вам слід уникати використання конструктора String за будь-яку ціну.
hfontanez

щоб відобразити один байт до одного знака (з 8859-1) і без винятку обробки (з nio.charset):String str = new String(bytes, java.nio.charset.StandardCharsets.ISO_8859_1);
iman

1
з Java 1.7, ви можете використовувати новий String (байти, StandardCharsets.UTF_8)
ihebiheb

101

"Правильна конверсія" між byte[]і Stringполягає в явній заяві кодування, яке ви хочете використовувати. Якщо ви почнете з а, byte[]а насправді він не містить текстових даних, не буде "належного перетворення". Strings - це текст, byte[]призначений для двійкових даних, і єдине розумне, що потрібно зробити, - це уникати перетворення між ними, якщо ви абсолютно не повинні.

Якщо ви дійсно повинні використовувати a Stringдля зберігання двійкових даних, то найбезпечнішим способом є використання кодування Base64 .


1
Так, кодування символів - це те, про що потрібно знати, щоб конвертувати між рядками та байтами.
Raedwald

4
Base64 і ти врятував мені життя
mstzn

2
Кодування Base64 вирішило мою проблему. UTF-8 працював не на всіх входах
Аль-Аламін

37

Основна проблема полягає в тому, що ви мимоволі використовуєте набір символів, для якого:

 bytes != encode(decode(bytes))

В деяких випадках. UTF-8 - приклад такого набору символів. Зокрема, певні послідовності байтів не є дійсними кодуваннями в UTF-8. Якщо декодер UTF-8 стикається з однією з цих послідовностей, він може відкинути оскаржуючі байти або розшифрувати їх як кодову точку Unicode для "немає такого символу". Природно, коли ви спробуєте кодувати символи як байти, результат буде іншим.

Рішення таке:

  1. Будьте чіткими щодо кодування символів, який ви використовуєте; тобто використовувати конструктор String та String.toByteArrayметод із явним кодом.
  2. Використовуйте правильний набір символів для даних байтів ... або альтернативно один (наприклад, "Latin-1", де всі послідовності байтів відображаються на дійсні символи Unicode.
  3. Якщо ваші байти - це (дійсно) двійкові дані, і ви хочете мати можливість передавати / отримувати їх по "текстовому" каналу, використовуйте щось на зразок кодування Base64 ..., яке призначене для цієї мети .

1
Дякуємо за пораду використання кодування "Latin-1"!
Гонзо

31

Нам просто потрібно побудувати новий Stringз масивом: http://www.mkyong.com/java/how-do-convert-byte-array-to-string-in-java/

String s = new String(bytes);

Байти отриманого рядка відрізняються залежно від того, яку діаграму ви використовуєте. нова String (байти) та нова String (байти, Charset.forName ("utf-8")) та нова String (байти, Charset.forName ("utf-16")) матимуть різні байтові масиви при виклику String # getBytes () (залежно від діаграми за замовчуванням)


9
Ні. Байти отриманого рядка відрізняються залежно від того, яку діаграму ви використовуєте. new String(bytes)і new String(bytes, Charset.forName("utf-8"))і new String(bytes, Charset.forName("utf-16"))всі матимуть різні масиви байтів при виклику String#getBytes()( в залежності від кодування за замовчуванням)
NS - дю Туа

1
Оманливий оман. charЗ (і , таким чином текст , який відображається) отриманих Stringвідрізняється при декодуванні по- bytesрізному. Перетворення назад в байти з використанням кодування за замовчуванням (використовуйте String#getBytes("charset")для вказання іншого) обов'язково відрізнятиметься, оскільки воно перетворює різний вхід. Строки не зберігають те, з чого byte[]вони були зроблені, chars не мають кодування, а а Stringне зберігає його інакше.
zapl

14

Використання new String(byOriginal)та повернення до byte[]використання getBytes()не гарантує двох byte[]із рівними значеннями. Це пов'язано з викликом , StringCoding.encode(..)який буде кодувати Stringв Charset.defaultCharset(). Під час цього кодування кодер може вибрати заміну невідомих символів та внести інші зміни. Отже, використання String.getBytes()може не повернути рівномірний масив, як ви спочатку передали конструктору.


9

Чому виникла проблема: Як хтось уже вказав: Якщо ви починаєте з байту [], а він фактично не містить текстових даних, немає "належного перетворення". Рядки для тексту, байт [] - для двійкових даних, і єдине дійсно розумне, що потрібно зробити, - це уникати перетворення між ними, якщо вам абсолютно не потрібно.

Я спостерігав за цією проблемою, коли я намагався створити байт [] з pdf-файлу, а потім перетворив його в String, а потім взяв String як вхід і перетворив назад у файл.

Тому переконайтесь, що ваша логіка кодування та декодування така сама, як я. Я явно закодував байт [] до Base64 і розшифрував його, щоб створити файл знову.

Використання регістра: З - за деяких обмежень я намагався відправити byte[]в request(POST)і процес наступним чином :

Файл PDF >> Base64.encodeBase64 (byte []) >> String >> Відправити запит (POST) >> отримати String >> Base64.decodeBase64 (byte []) >> create binary

Спробуйте це, і це працювало для мене ..

File file = new File("filePath");

        byte[] byteArray = new byte[(int) file.length()];

        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            fileInputStream.read(byteArray);

            String byteArrayStr= new String(Base64.encodeBase64(byteArray));

            FileOutputStream fos = new FileOutputStream("newFilePath");
            fos.write(Base64.decodeBase64(byteArrayStr.getBytes()));
            fos.close();
        } 
        catch (FileNotFoundException e) {
            System.out.println("File Not Found.");
            e.printStackTrace();
        }
        catch (IOException e1) {
            System.out.println("Error Reading The File.");
            e1.printStackTrace();
        }

6

Це добре для мене:

String cd="Holding some value";

Перетворення з рядка в байт []:

byte[] cookie = new sun.misc.BASE64Decoder().decodeBuffer(cd);

Перетворення з байту [] в рядок:

cd = new sun.misc.BASE64Encoder().encode(cookie);

5
private static String toHexadecimal(byte[] digest){
        String hash = "";
    for(byte aux : digest) {
        int b = aux & 0xff;
        if (Integer.toHexString(b).length() == 1) hash += "0";
        hash += Integer.toHexString(b);
    }
    return hash;
}

1
Це не відповідає на запитання.
james.garriss

Не відповідає на питання, але був корисним +1
Lazy Ninja

5

Я помітив щось, чого немає ні в одній з відповідей. Ви можете надати кожен байт у байтовому масиві символам та помістити їх у масив char. Тоді рядок є

new String(cbuf)
де cbuf - це масив char. Щоб перетворити назад, проведіть цикл через рядок, що переводить кожну з символів у байти, щоб помістити в байтовий масив, і цей байтовий масив буде таким же, як і перший.


public class StringByteArrTest {

    public static void main(String[] args) {
        // put whatever byte array here
        byte[] arr = new byte[] {-12, -100, -49, 100, -63, 0, -90};
        for (byte b: arr) System.out.println(b);
        // put data into this char array
        char[] cbuf = new char[arr.length];
        for (int i = 0; i < arr.length; i++) {
            cbuf[i] = (char) arr[i];
        }
        // this is the string
        String s = new String(cbuf);
        System.out.println(s);

        // converting back
        byte[] out = new byte[s.length()];
        for (int i = 0; i < s.length(); i++) {
            out[i] = (byte) s.charAt(i);
        }
        for (byte b: out) System.out.println(b);
    }

}

2

javax.xml.bind.DatatypeConverter повинен це зробити:

byte [] b = javax.xml.bind.DatatypeConverter.parseHexBinary("E62DB");
String s = javax.xml.bind.DatatypeConverter.printHexBinary(b);

2

Ось кілька методів, які перетворюють масив байтів у рядок. Я перевірив їх, вони працюють добре.

public String getStringFromByteArray(byte[] settingsData) {

    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(settingsData);
    Reader reader = new BufferedReader(new InputStreamReader(byteArrayInputStream));
    StringBuilder sb = new StringBuilder();
    int byteChar;

    try {
        while((byteChar = reader.read()) != -1) {
            sb.append((char) byteChar);
        }
    }
    catch(IOException e) {
        e.printStackTrace();
    }

    return sb.toString();

}

public String getStringFromByteArray(byte[] settingsData) {

    StringBuilder sb = new StringBuilder();
    for(byte willBeChar: settingsData) {
        sb.append((char) willBeChar);
    }

    return sb.toString();

}

2

Незважаючи на

new String(bytes, "UTF-8")

правильно, він кидає, UnsupportedEncodingExceptionщо змушує вас мати справу з перевіреним винятком. Ви можете використовувати в якості альтернативи інший конструктор, починаючи з Java 1.6 для перетворення байтового масиву в String:

new String(bytes, StandardCharsets.UTF_8)

Цей не викидає жодного винятку.

Перетворення назад також слід здійснити за допомогою StandardCharsets.UTF_8:

"test".getBytes(StandardCharsets.UTF_8)

Знову вам не доведеться мати справу з перевіреними винятками.


1

Цим методом мені вдалося перетворити байтовий масив у рядок:

public static String byteArrayToString(byte[] data){
    String response = Arrays.toString(data);

    String[] byteValues = response.substring(1, response.length() - 1).split(",");
    byte[] bytes = new byte[byteValues.length];

    for (int i=0, len=bytes.length; i<len; i++) {
        bytes[i] = Byte.parseByte(byteValues[i].trim());
    }

    String str = new String(bytes);
    return str.toLowerCase();
}

1

Хоча кодування base64 є безпечним і можна стверджувати "правильну відповідь", я приїхав сюди, шукаючи спосіб перетворити байтовий масив Java в / з Java String як такий. Тобто, коли кожен член байтового масиву залишається неушкодженим у своєму рядку String, не маючи додаткового місця для кодування / транспорту.

Ця відповідь, що описує 8-бітну прозору кодування, була дуже корисною для мене. Я використовував ISO-8859-1на терабайтах двійкових даних для успішного перетворення назад і назад (двійковий <-> рядок) без завищених вимог місця, необхідних для кодування base64, тому безпечний для мого випадку використання - YMMV.

Це також було корисним для пояснення, коли / якщо слід експериментувати.


0
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;    

private static String base64Encode(byte[] bytes)
{
    return new BASE64Encoder().encode(bytes);
}

private static byte[] base64Decode(String s) throws IOException
{
    return new BASE64Decoder().decodeBuffer(s);
}

Чому? Навіщо пройти Base64 для того, щоб перетворити байт в String? Накладні.
james.garriss

0

Ось робочий код.

            // Encode byte array into string . TemplateBuffer1 is my bytearry variable.

        String finger_buffer = Base64.encodeToString(templateBuffer1, Base64.DEFAULT);
        Log.d(TAG, "Captured biometric device->" + finger_buffer);


        // Decode String into Byte Array. decodedString is my bytearray[] 
        decodedString = Base64.decode(finger_buffer, Base64.DEFAULT);


-1

Прочитайте байти від Stringвикористання ByteArrayInputStreamта оберніть їх, BufferedReaderякий є потоком Char замість потоку байт, який перетворює дані байтів у String.

package com.cs.sajal;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

public class TestCls {

    public static void main(String[] args) {

        String s=new String("Sajal is  a good boy");

        try
        {
        ByteArrayInputStream bis;
        bis=new ByteArrayInputStream(s.getBytes("UTF-8"));

        BufferedReader br=new BufferedReader(new InputStreamReader(bis));
        System.out.println(br.readLine());

        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

    }
}

Вихід:

Саджал - хороший хлопчик


-1

Ви можете використовувати прості для циклу для перетворення:

public void byteArrToString(){
   byte[] b = {'a','b','$'};
   String str = ""; 
   for(int i=0; i<b.length; i++){
       char c = (char) b[i];
       str+=c;
   }
   System.out.println(str);
}


-3

Рядок - це набір знаків char (16-бітний без підпису). Тож якщо ви збираєтеся перетворити негативні числа в рядок, вони втратяться при перекладі.


1
-1: Це неправильно. Хоча 'байт' є підписаним типом на Java, вони розглядаються як ненаписані кодом бібліотеки, що робить кодування і декодування набору символів.
Stephen C

Чудовий приклад того, чому насправді мати неподписаний 8-бітовий тип даних - це гарна ідея мати мову. Уникає зайвої плутанини; ^)
жаба

Будьте обережні, припускаючи, що в Java-символі буде 16 біт, оскільки Java UTF-16 може розширити до 32 біт
Джо Плант

1
@Toad насправді так, деякі символи Unicode, зберігаються як UTF-16, займають дві кодові точки, тобто 32 біти. Те саме відбувається в UTF-8: деякі символи використовують два / три / чотири кодові точки, тобто 16/24/32 біт. Насправді саме в цьому і полягає UTF (тобто UTF! = Unicode).
CAFxX

1
@Toad ви отримаєте перший сурогат - тобто лише першу "половину" персонажа. Подивіться на документи для методу String.charAt та класу символів .
CAFxX

-3
public class byteString {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        String msg = "Hello";
        byte[] buff = new byte[1024];
        buff = msg.getBytes("UTF-8");
        System.out.println(buff);
        String m = new String(buff);
        System.out.println(m);


    }

}

Передайте Кодування Шарсетів як аргумент, щоб отриматиBytes
Shyam

1
Можливо, ви хочете розглянути деталізацію цієї відповіді з поясненням на додаток до коду.
Чарлі Шліссер

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.