Jak przekonwertować tablicę bajtów na ciąg znaków i odwrotnie?


248

Muszę przekonwertować tablicę bajtów na ciąg znaków w Androidzie, ale moja tablica bajtów zawiera wartości ujemne.

Jeśli ponownie przekonwertuję ten ciąg na tablicę bajtów, wartości, które otrzymuję, będą się różnić od oryginalnych wartości w tablicy bajtów.

Co mogę zrobić, aby uzyskać właściwą konwersję? Kod, którego używam do konwersji, jest następujący:

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

Utknąłem w tym problemie.


3
Dlaczego w pierwszej kolejności próbujesz przekonwertować dowolne dane binarne na ciąg? Oprócz wszystkich problemów z charsetem, o których już wspominają odpowiedzi, istnieje również fakt, że w tym przypadku nadużywasz Stringa. Co jest złego w używaniu a byte[]do danych binarnych i Stringtekstu?
Joachim Sauer

8
@Joachim - czasami masz zewnętrzne narzędzia, które mogą robić takie rzeczy jak ciągi sklepowe. W takim przypadku chcesz mieć możliwość przekształcenia tablicy bajtów w (zakodowany w jakiś sposób) ciąg.
James Moore,

Odpowiedzi:


377

Twoja tablica bajtów musi mieć pewne kodowanie. Kodowanie nie może być ASCII, jeśli masz wartości ujemne. Kiedy to zrozumiesz, możesz przekonwertować zestaw bajtów na ciąg znaków, używając:

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

Istnieje kilka kodowań można użyć, spojrzenie na klasy Charset w Javadocs Sun .


4
@MauricePerry czy możesz wyjaśnić, dlaczego to nie działa UTF-8?
Asif Mushtaq,

12
@UnKnown, ponieważ UTF-8 koduje niektóre znaki jako ciągi 2- lub 3- bajtowe. Nie każda tablica bajtów jest prawidłowym łańcuchem zakodowanym w UTF-8. Lepszym wyborem byłaby ISO-8859-1: tutaj każda postać jest kodowana jako bajt.
Maurice Perry,

1
Może to działać, ale należy unikać korzystania z konstruktora ciągów za wszelką cenę.
hfontanez

aby zamapować jeden bajt na jeden znak (z 8859-1) i bez obsługi wyjątków (z nio.charset):String str = new String(bytes, java.nio.charset.StandardCharsets.ISO_8859_1);
iman

1
od wersji Java 1.7 można używać nowego ciągu (bajty, StandardCharsets.UTF_8)
ihebiheb

101

„Właściwa konwersja” pomiędzy byte[]i Stringpolega na jawnym określeniu kodowania, którego chcesz użyć. Jeśli zaczynasz od a byte[]i tak naprawdę nie zawiera danych tekstowych, nie ma „właściwej konwersji”. Strings są dla tekstu, byte[]są dla danych binarnych, a jedyną naprawdę rozsądną rzeczą jest unikanie konwersji między nimi, chyba że absolutnie musisz.

Jeśli naprawdę musisz użyć a Stringdo przechowywania danych binarnych, najbezpieczniejszym sposobem jest użycie kodowania Base64 .


1
Tak, kodowanie znaków jest czymś, o czym musisz wiedzieć, aby przekonwertować ciągi znaków i bajty.
Raedwald,

4
Base64 i uratowałeś mi życie
mstzn

2
Kodowanie Base64 rozwiązało mój problem. UTF-8 nie działał dla wszystkich danych wejściowych
Al-Alamin,

37

Głównym problemem jest (myślę), że nieświadomie używasz zestawu znaków, dla którego:

 bytes != encode(decode(bytes))

w niektórych przypadkach. UTF-8 jest przykładem takiego zestawu znaków. W szczególności niektóre sekwencje bajtów nie są poprawnymi kodowaniami w UTF-8. Jeśli dekoder UTF-8 napotka jedną z tych sekwencji, może odrzucić błędne bajty lub zdekodować je jako kodowanie Unicode na „brak takiego znaku”. Oczywiście, kiedy spróbujesz zakodować znaki jako bajty, wynik będzie inny.

Rozwiązaniem jest:

  1. Wyraźnie mów o używanym kodowaniu znaków; tzn. użyj konstruktora i String.toByteArraymetody String z jawnym zestawem znaków.
  2. Użyj odpowiedniego zestawu znaków dla danych bajtów ... lub alternatywnie jednego (takiego jak „Latin-1”, gdzie wszystkie sekwencje bajtów są odwzorowane na prawidłowe znaki Unicode.
  3. Jeśli twoje bajty to (naprawdę) dane binarne i chcesz mieć możliwość ich przesyłania / odbierania kanałem „tekstowym”, użyj czegoś takiego jak kodowanie Base64 ... które jest przeznaczone do tego celu .

1
Dziękujemy za wskazówkę dotyczącą kodowania „Latin-1”!
Gonzo

31

Musimy tylko zbudować nowy Stringz tablicą: http://www.mkyong.com/java/how-do-convert-byte-array-to-string-in-java/

String s = new String(bytes);

Bajty wynikowego ciągu różnią się w zależności od używanego zestawu znaków. nowy ciąg (bajty) i nowy ciąg (bajty, Charset.forName („utf-8”)) i nowy ciąg (bajty, Charset.forName („utf-16”)) będą miały różne tablice bajtów po wywołaniu ciągu # getBytes () (w zależności od domyślnego zestawu znaków)


9
Nie. Bajty wynikowego ciągu różnią się w zależności od używanego zestawu znaków. new String(bytes)i new String(bytes, Charset.forName("utf-8"))i new String(bytes, Charset.forName("utf-16"))będą miały różne tablice bajtów podczas rozmowy String#getBytes()(w zależności od domyślnego kodowania)
NS du Toit

1
Zwodniczy. Wynik char(i tym samym wyświetlany tekst) wynikowy Stringróżni się przy bytesodmiennym dekodowaniu . Konwersja z powrotem na bajty przy użyciu domyślnego kodowania (użyj, String#getBytes("charset")aby określić inaczej) będzie się różnić, ponieważ konwertuje różne dane wejściowe. Ciągi nie przechowują, z byte[]których zostały wykonane, charnie mają kodowania i Stringnie przechowują go inaczej.
zapl

14

Używanie new String(byOriginal)i powrót do byte[]używania getBytes()nie gwarantuje dwóch byte[]z jednakowymi wartościami. Wynika to z połączenia zStringCoding.encode(..) których będą kodować Stringdo Charset.defaultCharset(). Podczas tego kodowania koder może zastąpić nieznane znaki i wprowadzić inne zmiany. Dlatego użycie String.getBytes()może nie zwrócić równej tablicy, tak jak pierwotnie przekazałeś konstruktor.


9

Dlaczego problem: Jak ktoś już określił: jeśli zaczynasz od bajtu [] i tak naprawdę nie zawiera on danych tekstowych, nie ma „właściwej konwersji”. Ciągi znaków dotyczą tekstu, bajt [] dotyczy danych binarnych, a jedyną naprawdę rozsądną rzeczą jest unikanie konwersji między nimi, chyba że jest to absolutnie konieczne.

Obserwowałem ten problem, gdy próbowałem utworzyć bajt [] z pliku pdf, a następnie przekonwertować go na ciąg znaków, a następnie pobrać ciąg znaków jako wejściowy i przekonwertować z powrotem do pliku.

Upewnij się więc, że logika kodowania i dekodowania jest taka sama jak ja. Jawnie zakodowałem bajt [] do Base64 i zdekodowałem go, aby ponownie utworzyć plik.

Use-case: Ze względu na pewne ograniczenia starałem wysyłał byte[]w request(POST), a proces był następujący:

Plik PDF >> Base64.encodeBase64 (byte []) >> String >> Wyślij w żądaniu (POST) >> otrzymaj String >> Base64.decodeBase64 (byte []) >> utwórz plik binarny

Spróbuj tego i to zadziałało dla mnie ..

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

To działa dobrze dla mnie:

String cd="Holding some value";

Konwertowanie z ciągu na bajt []:

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

Konwertowanie z bajtu [] na ciąg znaków:

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
To nie odpowiada na pytanie.
james.garriss

Nie odpowiada na pytanie, ale był użyteczny +1
Lazy Ninja

5

Zauważyłem coś, czego nie ma w żadnej z odpowiedzi. Możesz rzutować każdy z bajtów z tablicy bajtów na znaki i umieścić je w tablicy char. Zatem ciąg jest

new String(cbuf)
gdzie cbuf jest tablicą znaków. Aby przekonwertować z powrotem, zapętl pętlę, przesyłając każdy z znaków do bajtów, aby umieścić je w tablicy bajtów, a ta tablica bajtów będzie taka sama jak pierwsza.


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 powinien to zrobić:

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

2

Oto kilka metod, które konwertują tablicę bajtów na ciąg. Przetestowałem je, działają dobrze.

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

Nawet jeśli

new String(bytes, "UTF-8")

jest poprawne, rzuca a, UnsupportedEncodingExceptionktóre zmusza cię do zajęcia się sprawdzonym wyjątkiem. Możesz użyć jako alternatywnego innego konstruktora od wersji Java 1.6 do konwersji tablicy bajtów na String:

new String(bytes, StandardCharsets.UTF_8)

Ten nie rzuca żadnych wyjątków.

Konwersję należy również wykonać za pomocą StandardCharsets.UTF_8:

"test".getBytes(StandardCharsets.UTF_8)

Ponownie unikaj konieczności sprawdzania wyjątków.


1

Przy pomocy tej metody udało mi się przekonwertować tablicę bajtów na ciąg znaków:

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

Podczas gdy kodowanie base64 jest bezpieczne i można argumentować „właściwą odpowiedź”, przybyłem tutaj, szukając sposobu na konwersję tablicy bajtów Java na / z Java String as-is. Oznacza to, że każdy element tablicy bajtów pozostaje nienaruszony w swoim odpowiedniku Łańcuch, bez potrzeby dodatkowego miejsca na kodowanie / transport.

Ta odpowiedź opisująca 8-bitowe przezroczyste kodowanie była dla mnie bardzo pomocna. użyłemISO-8859-1 terabajtów danych binarnych do pomyślnej konwersji tam iz powrotem (ciąg binarny <-> Ciąg) bez zawyżonej przestrzeni wymaganej do kodowania base64, więc jest bezpieczny dla mojego przypadku użycia - YMMV.

Pomogło to również w wyjaśnieniu, kiedy / czy powinieneś eksperymentować.


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

Czemu? Po co przechodzić przez Base64 w celu konwersji bajtu na ciąg? Koszty ogólne.
james.garriss

0

Oto działający kod.

            // 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

Spróbuj określić 8-bitowy zestaw znaków w obu konwersjach. Na przykład ISO-8859-1.


-1

Odczytaj bajty z Stringużycia ByteArrayInputStreami zawiń je, BufferedReaderktórym jest Strumień Char zamiast Strumienia Bajtów, który konwertuje dane bajtów na Ciąg.

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

    }
}

Dane wyjściowe to:

Sajal jest dobrym chłopcem


-1

Do konwersji można użyć prostej pętli 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

Łańcuch to zbiór znaków (16-bitowy bez znaku). Jeśli więc zamierzasz przekonwertować liczby ujemne na ciąg, zostaną one utracone podczas tłumaczenia.


1
-1: To jest nieprawidłowe. Podczas gdy „bajt” jest typem podpisanym w Javie, są one traktowane jako kod niepodpisany przez kod biblioteki, który koduje i dekoduje zestaw znaków.
Stephen C,

Dobry przykład, dlaczego posiadanie niepodpisanego 8-bitowego typu danych naprawdę jest dobrym pomysłem na język. Unika niepotrzebnego zamieszania; ^)
Ropucha

Zachowaj ostrożność przy zakładaniu, że znak Java będzie miał 16 bitów, ponieważ ze względu na UTF-16 Javy, mogą one rozszerzyć do 32 bitów
Joe Plante

1
@ Załaduj właściwie tak, niektóre znaki Unicode, gdy są przechowywane jako UTF-16, zajmują dwa punkty kodowe, tj. 32 bity. To samo dzieje się w UTF-8: niektóre znaki używają dwóch / trzech / czterech punktów kodowych, tj. 16/24/32 bitów. W rzeczywistości właśnie o to chodzi w UTF (tj. UTF! = Unicode).
CAFxX,

1
@ Załaduj, że dostaniesz pierwszy surogat - tj. Tylko pierwszą „połowę” postaci. Przejrzyj dokumentację dotyczącą metody String.charAt i klasy 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);


    }

}

Przekaż kodowanie Charset jako argument za getBytes
Shyam Sreenivasan

1
Możesz rozważyć uzupełnienie tej odpowiedzi wraz z wyjaśnieniem oprócz kodu.
Charlie Schliesser

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.