Làm thế nào để chuyển đổi mảng byte thành chuỗi và ngược lại?


247

Tôi phải chuyển đổi một mảng byte thành chuỗi trong Android, nhưng mảng byte của tôi chứa các giá trị âm.

Nếu tôi chuyển đổi chuỗi đó một lần nữa thành mảng byte, các giá trị tôi nhận được khác với các giá trị mảng byte gốc.

Tôi có thể làm gì để có được chuyển đổi phù hợp? Mã tôi đang sử dụng để thực hiện chuyển đổi như sau:

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

Tôi bị mắc kẹt trong vấn đề này.


3
Tại sao bạn lại cố gắng chuyển đổi dữ liệu nhị phân tùy ý thành Chuỗi ở vị trí đầu tiên? Ngoài tất cả các vấn đề về bộ ký tự mà các câu trả lời đã đề cập, còn có một thực tế là bạn đang lạm dụng String nếu bạn làm điều này. Có gì sai khi sử dụng byte[]dữ liệu nhị phân và Stringvăn bản của bạn?
Joachim Sauer

8
@Joachim - đôi khi bạn có các công cụ bên ngoài có thể làm những việc như chuỗi cửa hàng. Bạn muốn có thể biến một mảng byte thành một chuỗi (được mã hóa theo một cách nào đó) trong trường hợp đó.
James Moore

Câu trả lời:


377

Mảng byte của bạn phải có một số mã hóa. Mã hóa không thể là ASCII nếu bạn có giá trị âm. Khi bạn tìm ra điều đó, bạn có thể chuyển đổi một tập hợp byte thành Chuỗi bằng cách sử dụng:

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

Có một loạt các mã hóa mà bạn có thể sử dụng, hãy xem lớp Charset trong Sun javadocs .


4
@MauricePerry bạn có thể giải thích lý do tại sao nó không hoạt động với UTF-8?
Asif Mushtaq

12
@UnKnown vì UTF-8 mã hóa một số ký tự dưới dạng chuỗi 2 hoặc 3 byte. Không phải mọi mảng byte là một chuỗi được mã hóa UTF-8 hợp lệ. ISO-8859-1 sẽ là một lựa chọn tốt hơn: ở đây mỗi ký tự được mã hóa dưới dạng một byte.
Maurice Perry

1
Điều này có thể hoạt động, nhưng bạn nên tránh sử dụng String constructor bằng mọi giá.
hfontanez

để ánh xạ một byte thành một char (với 8859-1) và không xử lý ngoại lệ (với nio.charset):String str = new String(bytes, java.nio.charset.StandardCharsets.ISO_8859_1);
iman

1
kể từ Java 1.7, bạn có thể sử dụng Chuỗi mới (byte, StandardCharsets.UTF_8)
ihebiheb

101

"Chuyển đổi phù hợp" giữa byte[]Stringlà để nêu rõ mã hóa bạn muốn sử dụng. Nếu bạn bắt đầu bằng một byte[]và trên thực tế nó không chứa dữ liệu văn bản, thì không có "chuyển đổi phù hợp". Strings dành cho văn bản, byte[]dành cho dữ liệu nhị phân và điều thực sự hợp lý duy nhất cần làm là tránh chuyển đổi giữa chúng trừ khi bạn thực sự phải làm.

Nếu bạn thực sự phải sử dụng Stringđể giữ dữ liệu nhị phân thì cách an toàn nhất là sử dụng hóa Base64 .


1
Có, mã hóa ký tự là thứ bạn phải biết để chuyển đổi giữa các chuỗi và byte.
Raedwald

4
Base64 và bạn đã cứu mạng tôi
mstzn

2
Mã hóa Base64 đã giải quyết vấn đề của tôi. UTF-8 không hoạt động cho tất cả các đầu vào
Al-Alamin

37

Vấn đề gốc là (tôi nghĩ) rằng bạn đang vô tình sử dụng một bộ ký tự cho:

 bytes != encode(decode(bytes))

trong vài trường hợp. UTF-8 là một ví dụ về bộ ký tự như vậy. Cụ thể, các chuỗi byte nhất định không phải là mã hóa hợp lệ trong UTF-8. Nếu bộ giải mã UTF-8 gặp một trong các chuỗi này, thì có thể loại bỏ các byte vi phạm hoặc giải mã chúng dưới dạng mã hóa Unicode cho "không có ký tự như vậy". Đương nhiên, khi bạn cố mã hóa các ký tự dưới dạng byte thì kết quả sẽ khác.

Giải pháp là:

  1. Hãy rõ ràng về mã hóa ký tự bạn đang sử dụng; tức là sử dụng hàm tạo String vàString.toByteArray phương thức với bộ ký tự rõ ràng.
  2. Sử dụng bộ ký tự bên phải cho dữ liệu byte của bạn ... hoặc một cách khác (chẳng hạn như "Latin-1" trong đó tất cả các chuỗi byte ánh xạ tới các ký tự Unicode hợp lệ.
  3. Nếu byte của bạn là (thực sự) dữ liệu nhị phân và bạn muốn có thể truyền / nhận chúng qua kênh "dựa trên văn bản", hãy sử dụng thứ gì đó như mã hóa Base64 ... được thiết kế cho mục đích này .

1
Cảm ơn mẹo sử dụng mã hóa "Latin-1"!
Gonzo

31

Chúng ta chỉ cần xây dựng một cái mới Stringvới mảng: http://www.mkyong.com/java/how-do-convert-byte-array-to-opes-in-java/

String s = new String(bytes);

Các byte của chuỗi kết quả khác nhau tùy thuộc vào bộ ký tự bạn sử dụng. Chuỗi mới (byte) và Chuỗi mới (byte, Charset.forName ("utf-8")) và Chuỗi mới (byte, Charset.forName ("utf-16")) đều sẽ có các mảng byte khác nhau khi bạn gọi Chuỗi # getBytes () (tùy thuộc vào bộ ký tự mặc định)


9
Không. Các byte của chuỗi kết quả khác nhau tùy thuộc vào bộ ký tự bạn sử dụng. new String(bytes)new String(bytes, Charset.forName("utf-8"))new String(bytes, Charset.forName("utf-16"))tất cả sẽ có mảng byte khác nhau khi bạn gọi String#getBytes()(tùy thuộc vào charset mặc định)
NS du Toit

1
Gây hiểu lầm. Các chars (và do đó các văn bản hiển thị) của các kết quả Stringkhác nhau khi giải mã byteskhác nhau. Việc chuyển đổi trở lại byte bằng cách sử dụng mã hóa mặc định (sử dụng String#getBytes("charset")để chỉ định khác) sẽ nhất thiết phải khác nhau vì nó chuyển đổi đầu vào khác nhau. Các chuỗi không lưu trữ byte[]chúng được tạo từ, charkhông có mã hóa và Stringkhông lưu trữ theo cách khác.
zapl

14

Sử dụng new String(byOriginal)và chuyển đổi trở lại byte[]sử dụng getBytes()không đảm bảo hai byte[]giá trị bằng nhau. Điều này là do một cuộc gọi StringCoding.encode(..)sẽ mã hóa Stringđến Charset.defaultCharset(). Trong quá trình mã hóa này, bộ mã hóa có thể chọn thay thế các ký tự không xác định và thực hiện các thay đổi khác. Do đó, việc sử dụng String.getBytes()có thể không trả về một mảng bằng nhau như ban đầu bạn đã chuyển cho hàm tạo.


9

Tại sao lại có vấn đề: Như ai đó đã chỉ định: Nếu bạn bắt đầu bằng một byte [] và thực tế nó không chứa dữ liệu văn bản, thì không có "chuyển đổi phù hợp". Các chuỗi dành cho văn bản, byte [] dành cho dữ liệu nhị phân và điều thực sự hợp lý duy nhất cần làm là tránh chuyển đổi giữa chúng trừ khi bạn thực sự phải làm.

Tôi đã quan sát vấn đề này khi tôi đang cố gắng tạo byte [] từ tệp pdf và sau đó chuyển đổi nó thành Chuỗi và sau đó lấy Chuỗi làm đầu vào và chuyển đổi trở lại tệp.

Vì vậy, hãy chắc chắn rằng mã hóa và giải mã logic của bạn giống như tôi đã làm. Tôi đã mã hóa rõ ràng byte [] thành Base64 và giải mã nó để tạo lại tệp.

Use-case: Do một số hạn chế Tôi đã cố gắng để gửi đi byte[]trong request(POST)và quá trình này là như sau:

Tệp PDF >> Base64.encodeBase64 (byte []) >> Chuỗi >> Gửi yêu cầu (POST) >> nhận Chuỗi >> Base64.decodeBase64 (byte []) >> tạo nhị phân

Hãy thử cái này và cái này hiệu quả với tôi ..

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

Cái này làm việc tốt cho tôi:

String cd="Holding some value";

Chuyển đổi từ chuỗi sang byte []:

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

Chuyển đổi từ byte [] thành chuỗi:

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

Điều này không trả lời câu hỏi.
james.garriss

Không trả lời câu hỏi nhưng rất hữu ích +1
Ninja lười biếng

5

Tôi đã nhận thấy một cái gì đó không có trong bất kỳ câu trả lời. Bạn có thể truyền từng byte trong mảng byte thành các ký tự và đặt chúng vào một mảng char. Sau đó, chuỗi là

new String(cbuf)
trong đó cbuf là mảng char. Để chuyển đổi trở lại, lặp qua chuỗi truyền từng ký tự thành byte để đặt vào một mảng byte và mảng byte này sẽ giống như chuỗi đầu tiên.


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 Hãy làm nó:

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

2

Đây là một vài phương thức chuyển đổi một mảng byte thành một chuỗi. Tôi đã thử chúng, chúng hoạt động tốt.

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

Mặc dù

new String(bytes, "UTF-8")

là chính xác, nó ném một UnsupportedEncodingExceptioncái mà buộc bạn phải đối phó với một ngoại lệ được kiểm tra. Bạn có thể sử dụng như một hàm tạo khác thay thế kể từ Java 1.6 để chuyển đổi một mảng byte thành String:

new String(bytes, StandardCharsets.UTF_8)

Điều này không ném bất kỳ ngoại lệ.

Chuyển đổi trở lại cũng nên được thực hiện với StandardCharsets.UTF_8:

"test".getBytes(StandardCharsets.UTF_8)

Một lần nữa bạn tránh phải đối phó với các ngoại lệ được kiểm tra.


1

Tôi đã thành công chuyển đổi mảng byte thành một chuỗi với phương thức này:

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

Mặc dù mã hóa base64 là an toàn và người ta có thể tranh luận "câu trả lời đúng", tôi đã đến đây để tìm cách chuyển đổi một mảng byte Java thành / từ một chuỗi Java như hiện trạng. Đó là, nơi mỗi thành viên của mảng byte vẫn còn nguyên vẹn trong bản sao Chuỗi của nó, không cần thêm không gian để mã hóa / vận chuyển.

Câu trả lời này mô tả mã hóa trong suốt 8 bit rất hữu ích cho tôi. Tôi đã sử dụngISO-8859-1 trên terabyte dữ liệu nhị phân để chuyển đổi qua lại thành công (chuỗi nhị phân <->) mà không cần các yêu cầu không gian bị thổi phồng cần thiết cho mã hóa base64, vì vậy rất an toàn cho trường hợp sử dụng của tôi - YMMV.

Điều này cũng hữu ích trong việc giải thích khi nào / nếu bạn nên thử nghiệm.


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

Tại sao? Tại sao phải đi qua Base64 để chuyển đổi một byte thành Chuỗi? Trên cao.
james.garriss

0

Đây là mã làm việc.

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

Cố gắng chỉ định bộ ký tự 8 bit trong cả hai chuyển đổi. ISO-8859-1 chẳng hạn.


-1

Đọc các byte từ Stringviệc sử dụng ByteArrayInputStreamvà bọc nó với BufferedReaderChar Stream thay vì Byte Stream chuyển đổi dữ liệu byte thành Chuỗi.

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

    }
}

Đầu ra là:

Sajal là một cậu bé tốt


-1

Bạn có thể sử dụng vòng lặp đơn giản để chuyển đổi:

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

Chuỗi là một tập hợp char (không dấu 16 bit). Vì vậy, nếu bạn định chuyển đổi số âm thành một chuỗi, chúng sẽ bị mất trong bản dịch.


1
-1: Điều này không chính xác. Mặc dù 'byte' là một loại đã ký trong Java, nhưng chúng được coi là không dấu bởi mã thư viện có mã hóa và giải mã ký tự.
Stephen C

Một ví dụ điển hình tại sao có một kiểu dữ liệu 8 bit không dấu thực sự là một ý tưởng tốt để có trong một ngôn ngữ. Tránh nhầm lẫn không cần thiết; ^)
cóc

Hãy cẩn thận khi cho rằng một char Java sẽ là 16 bit, vì UTF-16 của Java, chúng có thể mở rộng lên tới 32 bit
Joe Plante

1
@Toad thực sự có, một số ký tự Unicode khi được lưu dưới dạng UTF-16 chiếm hai điểm mã, tức là 32 bit. Điều tương tự cũng xảy ra trong UTF-8: một số ký tự sử dụng hai / ba / bốn điểm mã, tức là 16/24/32 bit. Trên thực tế, đó chính xác là những gì UTF nói về (ví dụ UTF! = Unicode).
CAFxX

1
@Toad bạn sẽ nhận được người thay thế đầu tiên - tức là chỉ "một nửa" đầu tiên của nhân vật. Nhìn vào các tài liệu cho phương thức String.charAt và lớp Ký tự .
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);


    }

}

Vượt qua Bộ mã hóa mã hóa để tranh luận về getBytes
Shyam Sreenivasan

1
Bạn có thể muốn xem xét đưa ra câu trả lời này với một lời giải thích bên cạnh mã.
Charlie Schliesser

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.