Cách chuyển đổi Chuỗi sang và từ mảng byte UTF8 trong Java


239

Trong Java, tôi có một Chuỗi và tôi muốn mã hóa nó dưới dạng một mảng byte (trong UTF8 hoặc một số mã hóa khác). Thay phiên, tôi có một mảng byte (trong một số mã hóa đã biết) và tôi muốn chuyển đổi nó thành Chuỗi Java. Làm cách nào để thực hiện các chuyển đổi này?

Câu trả lời:


355

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

String s = "some text here";
byte[] b = s.getBytes(StandardCharsets.UTF_8);

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

byte[] b = {(byte) 99, (byte)97, (byte)116};
String s = new String(b, StandardCharsets.US_ASCII);

Tất nhiên, bạn nên sử dụng tên mã hóa chính xác. Các ví dụ của tôi đã sử dụng US-ASCII và UTF-8, hai bảng mã phổ biến nhất.


30
US-ASCII thực sự không phải là một mã hóa rất phổ biến hiện nay. Windows-1252 và ISO-8859-1 (là siêu bộ của ASCII) phổ biến rộng rãi hơn nhiều.
Michael Borgwardt

11
Trên thực tế, tôi thấy nó khá phổ biến trong công việc của tôi. Tôi thường đọc các luồng byte có thể đã được lưu dưới dạng Windows-1252 hoặc ISO-8859-1 hoặc thậm chí là "đầu ra của chương trình kế thừa mà chúng tôi đã có trong 10 năm qua", nhưng chứa các byte được đảm bảo là hợp lệ Ký tự US-ASCII. Tôi cũng thường có một yêu cầu TẠO các tệp như vậy (để tiêu thụ theo mã có thể hoặc không thể xử lý các ký tự không phải ASCII. Về cơ bản, US-ASCII là "mẫu số chung lớn nhất" của nhiều phần mềm.
mcherm

1
Phương pháp này, tuy nhiên, sẽ không báo cáo bất kỳ vấn đề nào trong quá trình chuyển đổi. Đây có thể là những gì bạn muốn. Nếu không, nên sử dụng CharsetEncoder thay thế.
Michael Piefel

7
@Pacerier vì các tài liệu cho Charset liệt kê "UTF-8" là một trong những bảng mã tiêu chuẩn. Tôi tin rằng chính tả của bạn cũng được chấp nhận, nhưng tôi đã đi với những gì các tài liệu nói.
mcherm

20
Kể từ JDK7 bạn có thể sử dụng StandardCharsets.UTF_8 docs.oracle.com/javase/7/docs/api/java/nio/charset/...
Rafael Membrives

95

Đây là một giải pháp tránh thực hiện tra cứu Charset cho mọi chuyển đổi:

import java.nio.charset.Charset;

private final Charset UTF8_CHARSET = Charset.forName("UTF-8");

String decodeUTF8(byte[] bytes) {
    return new String(bytes, UTF8_CHARSET);
}

byte[] encodeUTF8(String string) {
    return string.getBytes(UTF8_CHARSET);
}

4
@mcherm: Ngay cả khi chênh lệch hiệu suất là nhỏ, tôi thích sử dụng các đối tượng (Bộ ký tự, URL, v.v.) hơn các dạng chuỗi của chúng khi có thể.
Bart van Heukelom

7
Lưu ý: Chuỗi công khai "Kể từ 1.6" (byte [] byte, bộ ký tự ký tự)
leo

1
Về "tránh thực hiện tra cứu Charset cho mỗi chuyển đổi" ... vui lòng trích dẫn một số nguồn. Không phải java.nio.charset.Charset được xây dựng trên đầu String.getBytes và do đó có nhiều chi phí hơn so với String.getBytes?
Pacerier

2
Các tài liệu nêu rõ: "Hành vi của phương thức này khi chuỗi này không thể được mã hóa trong bộ ký tự đã cho là không xác định. Lớp CharsetEncoder nên được sử dụng khi cần kiểm soát nhiều hơn quá trình mã hóa."
paiego

24
Lưu ý: kể từ Java 1.7, bạn có thể sử dụng StandardCharsets.UTF_8một cách liên tục để truy cập bộ ký tự UTF-8.
Kat

17
String original = "hello world";
byte[] utf8Bytes = original.getBytes("UTF-8");

Cảm ơn! Tôi đã viết nó lên một lần nữa bản thân tôi thêm hướng chuyển đổi khác.
mcherm

1
@smink Dấu gạch ngang trong không bắt buộc. Điều này sẽ sử dụng "UTF-8"
Mel Nicholson

14

Bạn có thể chuyển đổi trực tiếp thông qua phương thức khởi tạo String (byte [], String) và phương thức getBytes (String). Java trưng bày các bộ ký tự có sẵn thông qua lớp Charset . Tài liệu JDK liệt kê các bảng mã được hỗ trợ .

90% thời gian, các chuyển đổi như vậy được thực hiện trên các luồng, vì vậy bạn sẽ sử dụng các lớp Reader / Writer . Bạn sẽ không giải mã tăng dần bằng cách sử dụng các phương thức Chuỗi trên các luồng byte tùy ý - bạn sẽ để ngỏ cho các lỗi liên quan đến các ký tự đa dòng.


Bạn có thể xây dựng? Nếu ứng dụng của tôi mã hóa và giải mã Chuỗi UTF-8, thì mối quan tâm về các ký tự đa bào là gì?
raffian

@raffian Sự cố có thể xảy ra nếu bạn không chuyển đổi tất cả dữ liệu ký tự trong một lần. Xem ở đây cho một ví dụ.
McDowell

12

Việc triển khai tomcat7 của tôi đang chấp nhận các chuỗi là ISO-8859-1; mặc dù kiểu nội dung của yêu cầu HTTP. Giải pháp sau đây có hiệu quả với tôi khi cố gắng diễn giải chính xác các ký tự như 'é'.

byte[] b1 = szP1.getBytes("ISO-8859-1");
System.out.println(b1.toString());

String szUT8 = new String(b1, "UTF-8");
System.out.println(szUT8);

Khi cố gắng diễn giải chuỗi là US-ASCII, thông tin byte không được hiểu chính xác.

b1 = szP1.getBytes("US-ASCII");
System.out.println(b1.toString());

8
FYI, kể từ Java 7, bạn có thể sử dụng các hằng số cho các tên bộ ký tự như StandardCharSets.UTF_8, và StandardCharSets.ISO_8859_1.
Basil Bourque

Tiết kiệm trong ngày của tôi, làm việc hoàn toàn tốt cho giải pháp đầu tiên được đề cập ở trên.
Hassan Jamil

7

Thay thế, StringUtils từ Apache Commons có thể được sử dụng.

 byte[] bytes = {(byte) 1};
 String convertedString = StringUtils.newStringUtf8(bytes);

hoặc là

 String myString = "example";
 byte[] convertedBytes = StringUtils.getBytesUtf8(myString);

Nếu bạn có bộ ký tự không chuẩn, bạn có thể sử dụng getBytesUnchecked () hoặc newString () tương ứng.


4
Lưu ý rằng StringUtils này từ Commons Codec , không phải Commons Lang.
Arend v. Rebersdorff

Vâng, một chút của một gotcha! Đối với người dùng Gradle, Maven: "commons-codec: commons-codec: 1.10" (tại thời điểm viết). Ví dụ, điều này cũng được đóng gói như là một phụ thuộc với Apache POI. Ngoài việc Apache Commons đến giải cứu, như mọi khi!
mike gặm nhấm

2

Để giải mã một loạt byte thành thông điệp chuỗi bình thường, cuối cùng tôi đã làm cho nó hoạt động với mã hóa UTF-8 với mã này:

/* Convert a list of UTF-8 numbers to a normal String
 * Usefull for decoding a jms message that is delivered as a sequence of bytes instead of plain text
 */
public String convertUtf8NumbersToString(String[] numbers){
    int length = numbers.length;
    byte[] data = new byte[length];

    for(int i = 0; i< length; i++){
        data[i] = Byte.parseByte(numbers[i]);
    }
    return new String(data, Charset.forName("UTF-8"));
}

1

Nếu bạn đang sử dụng ASCII 7 bit hoặc ISO-8859-1 (một định dạng phổ biến đáng kinh ngạc) thì bạn hoàn toàn không phải tạo java.lang.String mới . Sẽ hiệu quả hơn nhiều khi chỉ cần chuyển byte thành char:

Ví dụ làm việc đầy đủ:

for (byte b : new byte[] { 43, 45, (byte) 215, (byte) 247 }) {
    char c = (char) b;
    System.out.print(c);
}

Nếu bạn không sử dụng các ký tự mở rộng như,, Å,, Ï, Ê có thể chắc chắn rằng các giá trị được truyền duy nhất là của 128 ký tự Unicode đầu tiên, thì mã này cũng sẽ hoạt động cho UTF-8 và ASCII mở rộng (như cp-1252).


1

Tôi không thể bình luận nhưng không muốn bắt đầu một chủ đề mới. Nhưng điều này không hiệu quả. Một chuyến đi khứ hồi đơn giản:

byte[] b = new byte[]{ 0, 0, 0, -127 };  // 0x00000081
String s = new String(b,StandardCharsets.UTF_8); // UTF8 = 0x0000, 0x0000,  0x0000, 0xfffd
b = s.getBytes(StandardCharsets.UTF_8); // [0, 0, 0, -17, -65, -67] 0x000000efbfbd != 0x00000081

Tôi cần b [] cùng một mảng trước và sau khi mã hóa nó không phải (điều này đề cập đến câu trả lời đầu tiên).


0
//query is your json   

 DefaultHttpClient httpClient = new DefaultHttpClient();
 HttpPost postRequest = new HttpPost("http://my.site/test/v1/product/search?qy=");

 StringEntity input = new StringEntity(query, "UTF-8");
 input.setContentType("application/json");
 postRequest.setEntity(input);   
 HttpResponse response=response = httpClient.execute(postRequest);

Chuỗi thực thể có chuyển đổi 'truy vấn' thành utf-8 hay chỉ cần nhớ khi đính kèm thực thể?
Cú pháp

0
Charset UTF8_CHARSET = Charset.forName("UTF-8");
String strISO = "{\"name\":\"א\"}";
System.out.println(strISO);
byte[] b = strISO.getBytes();
for (byte c: b) {
    System.out.print("[" + c + "]");
}
String str = new String(b, UTF8_CHARSET);
System.out.println(str);

0
Reader reader = new BufferedReader(
    new InputStreamReader(
        new ByteArrayInputStream(
            string.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));

-9

cực kỳ muộn nhưng tôi chỉ gặp phải vấn đề này và đây là cách khắc phục của tôi:

private static String removeNonUtf8CompliantCharacters( final String inString ) {
    if (null == inString ) return null;
    byte[] byteArr = inString.getBytes();
    for ( int i=0; i < byteArr.length; i++ ) {
        byte ch= byteArr[i]; 
        // remove any characters outside the valid UTF-8 range as well as all control characters
        // except tabs and new lines
        if ( !( (ch > 31 && ch < 253 ) || ch == '\t' || ch == '\n' || ch == '\r') ) {
            byteArr[i]=' ';
        }
    }
    return new String( byteArr );
}

2
Đầu tiên, nó không phải là một chuyển đổi: đó là loại bỏ các byte không in được. Thứ hai, nó giả định rằng mã hóa mặc định của HĐH cơ bản thực sự dựa trên ASCII cho các ký tự có thể in được (chẳng hạn sẽ không hoạt động trên Mainframes của IBM bằng EBCDIC).
Isaac
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.