Как преобразовать байтовый массив в строку и наоборот?


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, если у вас есть отрицательные значения. Как только вы это выясните, вы можете преобразовать набор байтов в строку, используя:

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

Есть куча кодировок , которые можно использовать, посмотрите на класс Charset в Javadocs Sun .


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

12
@UnKnown, потому что 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);
имана

1
начиная с Java 1.7, вы можете использовать новую строку (байты, StandardCharsets.UTF_8)
ihebiheb

101

«Правильное преобразование» между byte[]и Stringзаключается в явном указании кодировки, которую вы хотите использовать. Если вы начинаете с byte[]и на самом деле не содержат текстовые данные, там нет «надлежащего преобразования». StringОни предназначены для текста, byte[]для двоичных данных, и единственное, что действительно разумно сделать, - это избегать конвертации между ними, если только вам это не нужно.

Если вам действительно нужно использовать 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.toByteArrayметод String с явным набором символов.
  2. Используйте правильный набор символов для ваших байтовых данных ... или альтернативно (например, "Latin-1", где все последовательности байтов отображаются на действительные символы Юникода.
  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);

Байты получаемой строки различаются в зависимости от того, какую кодировку вы используете. new String (bytes) и new String (bytes, Charset.forName ("utf-8")) и new String (bytes, 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 du Toit

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

14

Использование new String(byOriginal)и преобразование обратно в byte[]использование getBytes()не гарантирует двух byte[]одинаковых значений. Это связано с вызовом , StringCoding.encode(..)который будет кодировать StringвCharset.defaultCharset() . Во время этой кодировки кодировщик может заменить неизвестные символы и внести другие изменения. Следовательно, использование String.getBytes()может не вернуть равный массив, который вы изначально передали конструктору.


9

Почему была проблема: Как кто-то уже указал: если вы начинаете с байта [], и он на самом деле не содержит текстовых данных, «правильного преобразования» не существует. Строки предназначены для текста, byte [] - для двоичных данных, и единственное, что действительно разумно сделать, - это избегать преобразования между ними, если в этом нет необходимости.

Я наблюдал эту проблему, когда пытался создать byte [] из PDF-файла, затем преобразовать его в String, а затем взять String в качестве входных данных и преобразовать обратно в файл.

Поэтому убедитесь, что ваша логика кодирования и декодирования такая же, как у меня. Я явно закодировал byte [] в Base64 и расшифровал его, чтобы снова создать файл.

Использование регистра: Из - за некоторых ограничений я пытался отправить byte[]в request(POST)и процесс следующим образом :

Файл PDF >> Base64.encodeBase64 (byte []) >> String >> Отправить в запросе (POST) >> получить строку >> Base64.decodeBase64 (byte []) >> создать двоичный файл

Попробуйте это, и это сработало для меня ..

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
Ленивый ниндзя

5

Я заметил что-то, чего нет ни в одном из ответов. Вы можете преобразовать каждый из байтов в массиве байтов в символы и поместить их в массив символов. Тогда строка

new String(cbuf)
где cbuf это массив символов. Чтобы выполнить обратное преобразование, переберите строку, преобразующую каждый из символов в байты для помещения в байтовый массив, и этот байтовый массив будет таким же, как и первый.


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, при этом для кодирования / транспорта не требуется дополнительного места.

Этот ответ, описывающий 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 для преобразования байта в строку? Накладные расходы.
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 Char вместо Byte Stream, который преобразует байтовые данные в 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

Вы можете использовать простой цикл for для преобразования:

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

Строка представляет собой набор символов (16-битный без знака). Так что если вы собираетесь конвертировать отрицательные числа в строку, они будут потеряны при переводе.


1
-1: это неверно Хотя «байт» является типом со знаком в Java, он рассматривается как неподписанный кодом библиотеки, который выполняет кодирование и декодирование набора символов.
Стивен С

Прекрасный пример того, почему наличие неподписанного 8-битного типа данных действительно хорошая идея для языка. Избегает ненужной путаницы; ^)
Жаба

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

1
@ На самом деле да, некоторые символы Юникода при сохранении в формате UTF-16 занимают две кодовые точки, то есть 32 бита. То же самое происходит в UTF-8: некоторые символы используют две / три / четыре кодовых точки, то есть 16/24/32 бит. Фактически, это именно то, о чем говорит UTF (то есть UTF! = Unicode).
CAFxX

1
@ Таким образом, вы получите первый суррогат - т.е. только первую «половину» персонажа. Посмотрите документы для метода String.charAt и класса Character .
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);


    }

}

Передайте кодировку Charset как аргумент для getBytes
Сринивасан

1
Вы можете рассмотреть возможность дополнения этого ответа объяснением в дополнение к коду.
Чарли Шлиссер

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.