Chuyển đổi mảng byte thành chuỗi (Java)


85

Tôi đang viết một ứng dụng web trong Google app Engine. Nó cho phép mọi người chỉnh sửa về cơ bản mã html được lưu trữ dưới dạng .htmltệp trong blobstore.

Tôi đang sử dụng fetchData để trả byte[]về tất cả các ký tự trong tệp. Tôi đang cố in ra html để người dùng chỉnh sửa mã html. Mọi thứ hoạt động tuyệt vời!

Đây là vấn đề duy nhất của tôi bây giờ:

Mảng byte đang gặp một số vấn đề khi chuyển đổi lại thành một chuỗi. Những câu trích dẫn thông minh và một vài ký tự được đưa ra trông rất thú vị. (? hoặc ký hiệu tiếng Nhật, v.v.) Cụ thể đó là một số byte tôi thấy có giá trị âm gây ra sự cố.

Các dấu ngoặc kép thông minh đang trở lại dưới dạng -108-109trong mảng byte. Tại sao lại như vậy và làm cách nào để giải mã các byte âm để hiển thị mã hóa ký tự chính xác?



Xin chào, tôi biết đó là một bài viết thực sự cũ nhưng tôi đang đối mặt với những vấn đề tương tự. Tôi đang tạo một proxy man-in-the-middle cho ssl. Vấn đề mà tôi đang gặp phải cũng giống như của bạn. Tôi lắng nghe socket và lấy dữ liệu vào InputStreamrồi vào byte[]. Bây giờ khi tôi đang cố gắng chuyển đổi byte[]thành Chuỗi (tôi cần sử dụng cơ quan phản hồi cho các cuộc tấn công), tôi nhận được các ký tự thực sự hài hước với đầy đủ các dấu ngoặc kép và dấu hỏi thông minh và những gì không. Tôi tin rằng bạn vấn đề là giống như tôi như cả hai chúng tôi đang đối phó với htmltrong byte[]. Bạn có thể vui lòng cho lời khuyên?
Parul S

Nhân tiện, tôi đã đi đến phạm vi để tìm mã hóa hệ thống của mình bằng Sytem.properties và thấy nó là "Cp1252". Bây giờ, tôi đã sử dụng String str=new String(buffer, "Cp1252");nhưng không được giúp đỡ.
Parul S

Câu trả lời:


141

Mảng byte chứa các ký tự trong một bảng mã đặc biệt (mà bạn nên biết). Cách để chuyển đổi nó thành một chuỗi là:

String decoded = new String(bytes, "UTF-8");  // example for one encoding type

By The Way - các byte thô xuất hiện có thể xuất hiện dưới dạng số thập phân âm chỉ vì kiểu dữ liệu java byteđược ký, nó bao gồm phạm vi từ -128 đến 127.


-109 = 0x93: Control Code "Set Transmit State"

Giá trị (-109) là ký tự điều khiển không in được trong UNICODE. Vì vậy UTF-8 không phải là mã hóa chính xác cho luồng ký tự đó.

0x93trong "Windows-1252" là "câu trích dẫn thông minh" mà bạn đang tìm kiếm, vì vậy tên Java của bảng mã đó là "Cp1252". Dòng tiếp theo cung cấp mã kiểm tra:

System.out.println(new String(new byte[]{-109}, "Cp1252")); 

5
Tôi đã thử sử dụng UTF-8 và nó vẫn xuất hiện như? 'S. Tại sao nó không tìm thấy ánh xạ cho những giá trị âm đó?
Josh

Tuy nhiên, 0x93 là một byte tiếp tục hợp lệ trong UTF-8 - sự hiện diện của byte đó chỉ loại trừ nó là UTF-8 nếu nó không đến sau một byte có hai bit đầu tiên được đặt.
Nick Johnson

1
@Josh Andreas giải thích tại sao - vì bytekiểu dữ liệu của Java đã được ký. Các giá trị 'âm' chỉ là các byte có bộ byte quan trọng nhất. Anh ấy cũng giải thích bộ ký tự khả dĩ nhất mà bạn nên sử dụng là - Windows-1252. Tuy nhiên, bạn nên biết bộ ký tự nào sẽ sử dụng từ ngữ cảnh hoặc quy ước mà không cần phải đoán.
Nick Johnson

25

Java 7 trở lên

Bạn cũng có thể chuyển mã hóa mong muốn của mình tới hàm Stringtạo dưới dạng Charsethằng số từ StandardCharsets . Điều này có thể an toàn hơn chuyển mã hóa dưới dạng a String, như được đề xuất trong các câu trả lời khác.

Ví dụ: đối với mã hóa UTF-8

String bytesAsString = new String(bytes, StandardCharsets.UTF_8);

1
Đây là một sự lặp lại của một câu trả lời từ 2011. -1
james.garriss

2
@ james.garriss Tôi không nghĩ vậy, trong chừng mực tôi chỉ đang đề cập đến một hàm tạo mới được giới thiệu trong java 7 cho phép mã hóa được truyền dưới dạng hằng số, theo ý kiến ​​của tôi là đẹp hơn và an toàn hơn api trước đó đã đề cập trong các câu trả lời trước đó nơi mã hóa được chuyển dưới dạng Chuỗi, nếu có.
davnicwil

11

Bạn có thể thử điều này.

String s = new String(bytearray);

9
Bạn có thể thử ... nhưng nó sẽ thất bại trong hầu hết mọi trường hợp.
Raedwald

5
public class Main {

    /**
     * Example method for converting a byte to a String.
     */
    public void convertByteToString() {

        byte b = 65;

        //Using the static toString method of the Byte class
        System.out.println(Byte.toString(b));

        //Using simple concatenation with an empty String
        System.out.println(b + "");

        //Creating a byte array and passing it to the String constructor
        System.out.println(new String(new byte[] {b}));

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Main().convertByteToString();
    }
}

Đầu ra

65
65
A

5
public static String readFile(String fn)   throws IOException 
{
    File f = new File(fn);

    byte[] buffer = new byte[(int)f.length()];
    FileInputStream is = new FileInputStream(fn);
    is.read(buffer);
    is.close();

    return  new String(buffer, "UTF-8"); // use desired encoding
}

3
Mã này sẽ làm rò rỉ tài nguyên nếu readném một ngoại lệ.
Raedwald

4

Tôi đề nghị Arrays.toString(byte_array);

Nó phụ thuộc vào mục đích của bạn. Ví dụ: tôi muốn lưu một mảng byte chính xác như định dạng mà bạn có thể thấy tại thời điểm gỡ lỗi, như sau: [1, 2, 3]Nếu bạn muốn lưu chính xác cùng một giá trị mà không chuyển các byte sang định dạng ký tự, hãy Arrays.toString (byte_array)thực hiện điều này ,. Nhưng nếu bạn muốn lưu ký tự thay vì byte, bạn nên sử dụng String s = new String(byte_array). Trong trường hợp này, stương đương với [1, 2, 3]định dạng ký tự.


Bạn có thể cung cấp thêm thông tin về lý do tại sao bạn đề xuất điều này? (Nó sẽ giải quyết vấn đề? Bạn có thể nói tại sao nó giải quyết được nó không?) Cảm ơn!
Dean J

Nó phụ thuộc vào mục đích của bạn. Ví dụ: tôi muốn lưu một mảng byte chính xác như định dạng mà bạn có thể thấy tại thời điểm gỡ lỗi, như sau: [1, 2, 3] Nếu bạn muốn lưu chính xác cùng một giá trị mà không cần chuyển đổi các byte sang định dạng ký tự, Arrays.toString (byte_array) thực hiện điều này,. Nhưng nếu bạn muốn lưu ký tự thay vì byte, bạn nên sử dụng String s = new String (byte_array). Trong trường hợp này, s tương đương với [1, 2, 3] ở định dạng ký tự.
hỏi

@sas, bạn nên thêm thông tin này vào câu trả lời của mình (bằng cách chỉnh sửa nó) thay vì dưới dạng nhận xét. Nói chung trên SO, bạn nên luôn nhớ rằng bất cứ lúc nào nhận xét có thể bị xóa - thông tin thực sự quan trọng phải nằm trong chính câu trả lời.
Jeen Broekstra

3

Câu trả lời trước từ Andreas_D là tốt. Tôi chỉ muốn nói thêm rằng bất cứ nơi nào bạn đang hiển thị đầu ra sẽ có phông chữ và mã hóa ký tự và nó có thể không hỗ trợ một số ký tự.

Để tìm ra vấn đề đó là Java hay màn hình của bạn, hãy làm như sau:

    for(int i=0;i<str.length();i++) {
        char ch = str.charAt(i);
        System.out.println(i+" : "+ch+" "+Integer.toHexString(ch)+((ch=='\ufffd') ? " Unknown character" : ""));
    }

Java sẽ ánh xạ bất kỳ ký tự nào mà nó không thể hiểu được thành 0xfffd ký tự chính thức cho các ký tự không xác định. Nếu bạn thấy dấu '?' trong đầu ra, nhưng nó không được ánh xạ tới 0xfffd, đó là vấn đề với phông chữ hiển thị hoặc mã hóa của bạn, không phải Java.

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.