Mã hóa chuỗi thành UTF-8


190

Tôi có một Chuỗi có ký tự "ñ" và tôi có một số vấn đề với nó. Tôi cần mã hóa Chuỗi này thành mã hóa UTF-8. Tôi đã thử nó bằng cách này, nhưng nó không hoạt động:

byte ptext[] = myString.getBytes();
String value = new String(ptext, "UTF-8");

Làm cách nào để mã hóa chuỗi đó thành utf-8?


2
Không rõ chính xác những gì bạn đang cố gắng làm. Liệu myString có chứa chính xác ký tự hay không và bạn có vấn đề khi chuyển đổi nó thành một mảng byte (trong trường hợp đó thấy câu trả lời từ Peter và Amir), hoặc myString bị hỏng và bạn đang cố gắng sửa nó (trong trường hợp đó, hãy xem câu trả lời từ Joachim và tôi)?
Michael Borgwardt

Tôi cần gửi myString đến một máy chủ có mã hóa utf-8 và tôi cần chuyển đổi ký tự "ñ" sang mã hóa utf-8.
Alex

1
Chà, nếu máy chủ đó mong đợi UTF-8 thì thứ bạn cần gửi là byte chứ không phải String. Vì vậy, theo câu trả lời của Peter, chỉ định mã hóa trong dòng đầu tiên và thả dòng thứ hai.
Michael Borgwardt

@Michael: Tôi đồng ý rằng không rõ ý định thực sự ở đây là gì. Dường như có rất nhiều câu hỏi mà mọi người đang cố gắng chuyển đổi rõ ràng giữa Chuỗi và byte thay vì để {In,Out}putStream{Read,Writ}ersnó làm điều đó cho họ. Tôi tự hỏi tại sao?
tchrist

1
@Michael: Cảm ơn, tôi cho rằng điều đó có ý nghĩa. Nhưng nó cũng làm cho nó khó hơn nó cần phải không? Tôi không thích ngôn ngữ hoạt động theo cách đó, và vì vậy hãy cố gắng tránh làm việc với chúng. Tôi nghĩ rằng mô hình Chuỗi ký tự của Java thay vì byte làm cho mọi thứ dễ dàng hơn nhiều. Perl và Python cũng chia sẻ tất cả mọi thứ trên mạng là mô hình chuỗi Unicode. Vâng, trong cả ba bạn vẫn có thể nhận được byte nếu bạn làm việc với nó, nhưng trong thực tế có vẻ hiếm khi bạn thực sự cần: đó là mức độ khá thấp. Thêm vào đó, nó cảm thấy giống như đánh một con mèo sai hướng, nếu bạn hiểu ý tôi. :)
tchrist

Câu trả lời:


140

String các đối tượng trong Java sử dụng mã hóa UTF-16 không thể sửa đổi.

Điều duy nhất có thể có một mã hóa khác nhau là a byte[]. Vì vậy, nếu bạn cần dữ liệu UTF-8, thì bạn cần a byte[]. Nếu bạn có một Stringdữ liệu chứa dữ liệu không mong muốn, thì vấn đề là ở một nơi nào đó trước đó đã chuyển đổi không chính xác một số dữ liệu nhị phân thành một String(tức là nó đang sử dụng mã hóa sai).


92
Về mặt kỹ thuật, byte [] không có bất kỳ mã hóa nào. Byte mảng PLUS mã hóa có thể cung cấp cho bạn chuỗi mặc dù.
Peter tibraný

1
@Peter: đúng. Nhưng việc gắn mã hóa vào nó chỉ có ý nghĩa byte[], nó không có ý nghĩa gì String(trừ khi mã hóa là UTF-16, trong trường hợp đó có ý nghĩa nhưng nó vẫn là thông tin không cần thiết).
Joachim Sauer

4
String objects in Java use the UTF-16 encoding that can't be modified. Bạn có một nguồn chính thức cho trích dẫn này?
Ahmad Hajjar

@AhmadHajjar docs.oracle.com/javase/10/docs/api/java/lang/ mẹo : "Nền tảng Java sử dụng biểu diễn UTF-16 trong mảng char và trong các lớp String và StringBuffer."
Maxi Gis

173

Cách sử dụng

ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(myString)

Xem cuộc thảo luận của tôi với Peter. Nhưng nếu giả định của anh ta về câu hỏi là đúng, giải pháp của bạn sẽ vẫn không có ý tưởng vì nó trả về ByteBuffer.
Michael Borgwardt

8
Nhưng làm thế nào để tôi có được một chuỗi được mã hóa? nó trả về một ByteBuffer
Alex

7
@Alex: không thể có Chuỗi Java được mã hóa UTF-8. Bạn muốn có byte, do đó, hãy sử dụng trực tiếp ByteBuffer (thậm chí có thể là giải pháp tốt nhất nếu mục tiêu của bạn là gửi nó qua bộ sưu tập mạng) hoặc gọi mảng () trên đó để lấy byte []
Michael Borgwardt

2
Một cái gì đó khác có thể hữu ích là sử dụng Guava's Charsets.UTF_8 enum thay vì Chuỗi có thể ném UnceptionedEncodingException. Chuỗi -> byte : myString.getBytes(Charsets.UTF_8), và byte -> Chuỗi : new String(myByteArray, Charsets.UTF_8).
laughing_man

24
Thậm chí tốt hơn, sử dụng StandardCharsets.UTF_8. Có sẵn trong Java 1.7+.
Kat

81

Trong Java7, bạn có thể sử dụng:

import static java.nio.charset.StandardCharsets.*;

byte[] ptext = myString.getBytes(ISO_8859_1); 
String value = new String(ptext, UTF_8); 

Điều này có lợi thế hơn getBytes(String)là nó không tuyên bố throws UnsupportedEncodingException.

Nếu bạn đang sử dụng phiên bản Java cũ hơn, bạn có thể tự khai báo các bộ ký tự:

import java.nio.charset.Charset;

public class StandardCharsets {
    public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
    public static final Charset UTF_8 = Charset.forName("UTF-8");
    //....
}

2
Đây là câu trả lời đúng. Nếu ai đó muốn sử dụng kiểu dữ liệu chuỗi, anh ta có thể sử dụng nó theo đúng định dạng. Phần còn lại của các câu trả lời đang trỏ đến loại định dạng byte.
Neeraj Shukla

Hoạt động trong 6. Cảm ơn.
Itsik Mauyhas

Câu trả lời đúng cho tôi quá. Mặc dù vậy, khi tôi sử dụng như trên, ký tự tiếng Đức đổi thành? Vì vậy, tôi đã sử dụng cái này: byte [] ptext = myString.getBytes (UTF_8); Chuỗi giá trị = Chuỗi mới (ptext, UTF_8); Điều này làm việc tốt.
Farhan Hafeez

3
Mẫu mã không có ý nghĩa. Nếu trước tiên bạn chuyển đổi thành ISO-8859-1, thì mảng byte đó không phải là UTF-8, vì vậy dòng tiếp theo hoàn toàn không chính xác. Tất nhiên, nó sẽ hoạt động với các chuỗi ASCII, nhưng sau đó bạn cũng có thể tạo một bản sao đơn giản : String value = new String(myString);.
Alexis Wilke

76

Sử dụng byte[] ptext = String.getBytes("UTF-8");thay vì getBytes(). getBytes()sử dụng cái gọi là "mã hóa mặc định", có thể không phải là UTF-8.


9
@Michael: anh ấy rõ ràng đang gặp sự cố khi nhận byte từ chuỗi. Làm thế nào là getBytes (mã hóa) thiếu điểm? Tôi nghĩ rằng dòng thứ hai là có để kiểm tra xem anh ta có thể chuyển đổi nó trở lại không.
Peter tibraný

1
Tôi hiểu nó là một Chuỗi bị hỏng và cố gắng "sửa" nó bằng cách chuyển đổi thành byte và ngược lại (hiểu lầm phổ biến). Không có dấu hiệu thực tế nào cho thấy dòng thứ hai chỉ đang kiểm tra kết quả.
Michael Borgwardt

@Michael, không, không có, đó chỉ là cách giải thích của tôi. Bạn chỉ đơn giản là khác nhau.
Peter tibraný

1
@Peter: bạn nói đúng, chúng tôi cần làm rõ từ Alex ý anh ấy thực sự là gì. Không thể hủy bỏ downvote trừ khi câu trả lời được chỉnh sửa ...
Michael Borgwardt

33

Chuỗi Java luôn được mã hóa bên trong UTF-16 - nhưng bạn thực sự nên nghĩ về nó như thế này: mã hóa là một cách để dịch giữa Chuỗi và byte.

Vì vậy, nếu bạn gặp vấn đề về mã hóa, vào thời điểm bạn có Chuỗi, đã quá muộn để khắc phục. Bạn cần sửa nơi bạn tạo Chuỗi đó từ tệp, DB hoặc kết nối mạng.


1
Đó là một lỗi phổ biến để tin rằng các chuỗi được mã hóa bên trong là UTF-16. Thông thường chúng là, nhưng nếu, nó chỉ là một chi tiết cụ thể thực hiện của lớp String. Do không thể truy cập bộ nhớ trong của dữ liệu ký tự thông qua API công khai, nên việc triển khai Chuỗi cụ thể có thể quyết định sử dụng bất kỳ mã hóa nào khác.
jarnbjo

3
@jarnbjo: API nói rõ ràng "Chuỗi đại diện cho một chuỗi ở định dạng UTF-16". Sử dụng bất cứ thứ gì khác làm định dạng nội bộ sẽ rất kém hiệu quả và tất cả các triển khai thực tế tôi biết đều sử dụng UTF-16 trong nội bộ. Vì vậy, trừ khi bạn có thể trích dẫn một cái không, bạn đang tham gia vào việc chia tóc khá vô lý.
Michael Borgwardt

Có phải là vô lý để phân biệt giữa truy cập công cộng và đại diện nội bộ của cấu trúc dữ liệu?
jarnbjo

5
JVM (theo như nó có liên quan đến VM) sử dụng UTF-8 để mã hóa chuỗi, ví dụ như trong các tệp lớp. Việc triển khai java.lang.String được tách rời khỏi JVM và tôi có thể dễ dàng triển khai lớp cho bạn bằng cách sử dụng bất kỳ mã hóa nào khác cho biểu diễn bên trong nếu điều đó thực sự cần thiết để bạn nhận ra rằng câu trả lời của bạn không chính xác. Sử dụng UTF-16 làm định dạng bên trong trong hầu hết các trường hợp rất kém hiệu quả khi sử dụng bộ nhớ và tôi không hiểu tại sao ví dụ triển khai Java cho phần cứng nhúng sẽ không tối ưu hóa cho bộ nhớ thay vì hiệu năng.
jarnbjo

1
@jarnbjo: Và một lần nữa: miễn là bạn không thể đưa ra một ví dụ cụ thể của một JVM có tiêu chuẩn API thực hiện không nội sử dụng một cái gì đó khác hơn là UTF-16 để thực hiện Strings, tuyên bố của tôi là đúng. Và không, lớp String không thực sự tách rời khỏi JVM, do những thứ như intern () và nhóm hằng số.
Michael Borgwardt

22

Bạn có thể thử theo cách này.

byte ptext[] = myString.getBytes("ISO-8859-1"); 
String value = new String(ptext, "UTF-8"); 

1
Tôi phát điên Cảm ơn bạn để có được các byte trong "ISO-8859-1" đầu tiên là giải pháp.
Gian Gomen

2
Cái này sai. Nếu chuỗi của bạn bao gồm các ký tự Unicode, việc chuyển đổi nó thành 8859-1 sẽ tạo ra một ngoại lệ hoặc tệ hơn là cung cấp cho bạn một chuỗi không hợp lệ (có thể là chuỗi không có các ký tự có mã điểm 0x100 trở lên).
Alexis Wilke

12

Trong một khoảnh khắc tôi đã trải qua vấn đề này và quản lý để giải quyết nó theo cách sau

đầu tiên tôi cần nhập

import java.nio.charset.Charset;

Sau đó, tôi đã phải khai báo một hằng số để sử dụng UTF-8ISO-8859-1

private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final Charset ISO = Charset.forName("ISO-8859-1");

Sau đó tôi có thể sử dụng nó theo cách sau:

String textwithaccent="Thís ís a text with accent";
String textwithletter="Ñandú";

text1 = new String(textwithaccent.getBytes(ISO), UTF_8);
text2 = new String(textwithletter.getBytes(ISO),UTF_8);

1
Giải pháp hoàn hảo.
Tunde Pizzle

9
String value = new String(myString.getBytes("UTF-8"));

và, nếu bạn muốn đọc từ tệp văn bản với mã hóa "ISO-8859-1":

String line;
String f = "C:\\MyPath\\MyFile.txt";
try {
    BufferedReader br = Files.newBufferedReader(Paths.get(f), Charset.forName("ISO-8859-1"));
    while ((line = br.readLine()) != null) {
        System.out.println(new String(line.getBytes("UTF-8")));
    }
} catch (IOException ex) {
    //...
}

2

Tôi đã sử dụng mã dưới đây để mã hóa ký tự đặc biệt bằng cách chỉ định định dạng mã hóa.

String text = "This is an example é";
byte[] byteText = text.getBytes(Charset.forName("UTF-8"));
//To get original string from byte.
String originalString= new String(byteText , "UTF-8");

2

Hướng dẫn từng bước nhanh về cách định cấu hình mã hóa mặc định UTB-8 của NetBeans. Kết quả là NetBeans sẽ tạo tất cả các tệp mới trong mã hóa UTF-8.

Hướng dẫn từng bước mã hóa NetFeans UTF-8

  • Chuyển đến thư mục vv trong thư mục cài đặt NetBeans

  • Chỉnh sửa tập tin netbeans.conf

  • Tìm dòng netbeans_default_options

  • Thêm -J-Dfile.encoding = UTF-8 bên trong dấu ngoặc kép bên trong dòng đó

    (ví dụ netbeans_default_options="-J-Dfile.encoding=UTF-8":)

  • Khởi động lại NetBeans

Bạn đặt mã hóa mặc định NetBeans UTF-8.

Netbeans_default_options của bạn có thể chứa các tham số bổ sung bên trong dấu ngoặc kép. Trong trường hợp như vậy, thêm -J-Dfile.encoding = UTF-8 ở cuối chuỗi. Tách nó với không gian từ các tham số khác.

Thí dụ:

netbeans_default_options = "- J-client -J-Xss128m -J-Xms256m -J-XX: PermSize = 32m -J-Dapple.laf.useScreenMothyBar = true -J-Dapple.awt.graphics.Use java2d.noddraw = true -J-Dsun.java2d.dpiaware = true -J-Dsun.zip.disableMemoryMapping = true -J-Dfile.encoding = UTF-8 "

đây là liên kết để biết thêm chi tiết


0

Điều này đã giải quyết vấn đề của tôi

    String inputText = "some text with escaped chars"
    InputStream is = new ByteArrayInputStream(inputText.getBytes("UTF-8"));
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.