Bagaimana mengkonversi Strings ke dan dari array byte UTF8 di Jawa

239

Di Jawa, saya memiliki sebuah String dan saya ingin menyandikannya sebagai byte array (dalam UTF8, atau beberapa pengkodean lainnya). Bergantian, saya memiliki array byte (dalam beberapa pengkodean dikenal) dan saya ingin mengubahnya menjadi String Java. Bagaimana saya melakukan konversi ini?

mcherm
sumber

Jawaban:

355

Konversi dari String ke byte []:

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

Konversi dari byte [] ke String:

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

Anda harus, tentu saja, menggunakan nama penyandian yang benar. Contoh saya menggunakan US-ASCII dan UTF-8, dua penyandian yang paling umum.

mcherm
sumber
30
US-ASCII sebenarnya bukan pengkodean yang sangat umum saat ini. Windows-1252 dan ISO-8859-1 (yang merupakan superset dari ASCII) jauh lebih luas.
Michael Borgwardt
11
Sebenarnya, saya menemukannya cukup umum dalam pekerjaan saya. Saya sering membaca aliran byte yang mungkin telah disimpan sebagai Windows-1252 atau ISO-8859-1 atau bahkan hanya sebagai "output dari program legacy yang kami miliki selama 10 tahun terakhir", tetapi yang mengandung byte dijamin akan valid Karakter US-ASCII. Saya juga sering memiliki persyaratan untuk MENGHASILKAN file seperti itu (untuk konsumsi dengan kode yang mungkin-atau-mungkin-tidak dapat menangani karakter non-ASCII. Pada dasarnya, AS-ASCII adalah "penyebut umum terbesar" dari banyak perangkat lunak.
mcherm
1
Namun, metode ini tidak akan melaporkan masalah apa pun dalam konversi. Ini mungkin yang Anda inginkan. Jika tidak, disarankan untuk menggunakan CharsetEncoder sebagai gantinya.
Michael Piefel
7
@Pacerier karena dokumen untuk daftar Charset "UTF-8" sebagai salah satu dari rangkaian karakter standar. Saya percaya bahwa ejaan Anda juga diterima, tetapi saya mengikuti apa yang dikatakan dokumen.
mcherm
20
Sejak JDK7 Anda dapat menggunakan StandardCharsets.UTF_8 docs.oracle.com/javase/7/docs/api/java/nio/charset/…
Rafael Membrives
95

Inilah solusi yang menghindari melakukan pencarian Charset untuk setiap konversi:

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);
}
Mike Leonhard
sumber
4
@mcherm: Sekalipun perbedaan kinerja kecil, saya lebih suka menggunakan objek (Charset, URL, dll) di atas bentuk string mereka bila memungkinkan.
Bart van Heukelom
7
Catatan: "Sejak 1,6" String publik (byte [] byte, charset charset)
leo
1
Mengenai "menghindari melakukan pencarian Charset untuk setiap konversi" ... silakan mengutip beberapa sumber. Bukankah java.nio.charset.Charset dibangun di atas String.getBytes dan karenanya memiliki overhead lebih banyak daripada String.getBytes?
Pacerier
2
Dokumen memang menyatakan: "Perilaku metode ini ketika string ini tidak dapat dikodekan dalam charset yang diberikan tidak ditentukan. Kelas CharsetEncoder harus digunakan ketika kontrol lebih besar atas proses pengkodean diperlukan."
paiego
24
Catatan: sejak Java 1.7, Anda dapat menggunakan StandardCharsets.UTF_8cara konstan untuk mengakses rangkaian karakter UTF-8.
Kat
17
String original = "hello world";
byte[] utf8Bytes = original.getBytes("UTF-8");
Jorge Ferreira
sumber
Terima kasih! Saya menulisnya lagi sendiri dan menambahkan arah konversi yang lain.
mcherm
1
@ Pandai dasbor di bukan opsional. Ini harus menggunakan "UTF-8"
Mel Nicholson
14

Anda dapat mengonversi secara langsung melalui konstruktor String (byte [], String) dan getBytes (String). Java memperlihatkan set karakter yang tersedia melalui kelas Charset . Dokumentasi JDK berisi daftar penyandian yang didukung .

90% dari waktu, konversi tersebut dilakukan pada aliran, sehingga Anda akan menggunakan kelas Reader / Writer . Anda tidak akan secara bertahap mendekode menggunakan metode String pada stream byte acak - Anda akan membiarkan diri Anda terbuka untuk bug yang melibatkan karakter multibyte.

McDowell
sumber
Bisakah Anda menguraikan? Jika aplikasi saya meng-encode dan mendekodekan Strings UTF-8, apa kekhawatiran tentang karakter multibytes?
raffian
@raffian Masalah dapat terjadi jika Anda tidak mengubah semua data karakter dalam sekali jalan. Lihat di sini untuk contoh.
McDowell
12

Implementasi tomcat7 saya menerima string sebagai ISO-8859-1; meskipun jenis konten permintaan HTTP. Solusi berikut ini berfungsi untuk saya ketika mencoba menafsirkan karakter seperti 'é' dengan benar.

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

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

Ketika mencoba menafsirkan string sebagai US-ASCII, info byte tidak diartikan dengan benar.

b1 = szP1.getBytes("US-ASCII");
System.out.println(b1.toString());
paiego
sumber
8
FYI, pada Java 7 Anda dapat menggunakan konstanta untuk nama-nama charset seperti StandardCharSets.UTF_8dan StandardCharSets.ISO_8859_1.
Basil Bourque
Menyelamatkan hari saya, bekerja dengan sangat baik untuk solusi pertama yang disebutkan di atas.
Hassan Jamil
7

Sebagai alternatif, StringUtils dari Apache Commons dapat digunakan.

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

atau

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

Jika Anda memiliki charset non-standar, Anda dapat menggunakan getBytesUnchecked () atau newString () .

Vtor
sumber
4
Perhatikan bahwa StringUtils ini dari Commons Codec , bukan Commons Lang.
Arend v. Reinersdorff
Ya, sedikit gotcha! Untuk pengguna Gradle, Maven: "commons-codec: commons-codec: 1.10" (saat penulisan). Ini juga dibundel sebagai ketergantungan dengan Apache POI, misalnya. Terlepas dari Apache Commons untuk menyelamatkan, seperti biasa!
mike rodent
2

Untuk mendekode serangkaian byte ke pesan string normal, akhirnya saya berhasil menggunakan pengkodean UTF-8 dengan kode ini:

/* 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"));
}
Bouke Woudstra
sumber
1

Jika Anda menggunakan 7-bit ASCII atau ISO-8859-1 (format yang luar biasa umum) maka Anda tidak perlu membuat java.lang.String sama sekali. Jauh lebih performant untuk memasukkan byte ke char:

Contoh kerja penuh:

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

Jika Anda tidak menggunakan karakter diperluas seperti Ä, Æ, Å, Ç, Ï, Ê dan dapat dipastikan bahwa nilai yang ditransmisikan hanya dari 128 karakter Unicode pertama, maka kode ini juga akan berfungsi untuk UTF-8 dan ASCII yang diperluas (seperti cp-1252).

Pacerier
sumber
1

Saya tidak dapat berkomentar tetapi tidak ingin memulai utas baru. Tapi ini tidak berhasil. Perjalanan pulang-pergi sederhana:

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

Saya perlu b [] array yang sama sebelum dan sesudah pengkodean yang bukan (rujukan ini untuk jawaban pertama).

jschober
sumber
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);
Ran Adler
sumber
Apakah String Entity mengonversi 'kueri' ke utf-8 atau hanya mengingat ketika melampirkan entitas?
SyntaxRules
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);
Nitish Raj Srivastava
sumber
0
Reader reader = new BufferedReader(
    new InputStreamReader(
        new ByteArrayInputStream(
            string.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
Макс Даниленко
sumber
-9

sangat terlambat tetapi saya baru saja mengalami masalah ini dan ini adalah perbaikan saya:

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 );
}
savio
sumber
2
Pertama, ini bukan konversi: ini adalah penghapusan byte yang tidak dapat dicetak. Kedua, ini mengasumsikan bahwa pengkodean default OS yang mendasari benar-benar didasarkan pada ASCII untuk karakter yang dapat dicetak (misalnya, tidak akan berfungsi pada Mainframe IBM menggunakan EBCDIC, misalnya).
Isaac