2011-01-24 56 views
10

Làm thế nào để chuyển đổi văn bản từ utf8/CP1251 (cửa sổ Cyrillic) để DOS Cyrillic (cp866)Java char bộ mã hóa vấn đề (từ UTF8 để cp866)

tôi thấy ví dụ này:

Charset fromCharset = Charset.forName("utf8"); 
Charset toCharset = Charset.forName("cp866"); 

String text1 = "Николай"; // my name in bulgarian 
String text2 = "Nikolay"; // my name in english 

System.out.println("TEXT1 :[" + toCharset.decode(fromCharset.encode(text1)).toString() + "]"); 
System.out.println("TEXT2 :[" + toCharset.decode(fromCharset.encode(text2)).toString() + "]"); 

Và đầu vào là:

TEXT1 :[╨Э╨╕╨║╨╛╨╗╨░╨╣] // WRONG 
TEXT2 :[Nikolay] // CORRECT 

Vấn đề ở đâu?

+2

Bạn mong đợi điều gì? Bạn đang mã hóa "Николай" bằng cách sử dụng UTF-8, sau đó giải mã các byte được mã hóa bằng cách sử dụng Cp866. Sản lượng có vẻ hợp lý với tôi, nhưng bạn rõ ràng mong đợi một số phép thuật khác xảy ra. – jarnbjo

Trả lời

5

Vấn đề là bạn đang cố gắng giải mã đầu ra của một mã hóa như thể nó là một mã khác.

Hãy tưởng tượng rằng bạn có một chương trình chỉ có thể ghi ra các ảnh JPEG và một chương trình khác chỉ đọc được PNG ... bạn có thể đọc đầu ra của chương trình đầu tiên bằng chương trình thứ hai không?

Trong trường hợp này, hai mã hóa xảy ra tương thích với các ký tự ASCII, nhưng về cơ bản bạn đang làm điều sai trái.

Nếu bạn có văn bản đã có trong UTF-8, bạn nên đọc từ dữ liệu nhị phân thành chuỗi Unicode sử dụng mã hóa UTF-8, sau đó ghi lại bằng cách sử dụng lại mã hóa khác đến nhị phân. Unicode là bước trung gian về cơ bản, như định dạng văn bản gốc của Java. Điều này tương đương với việc tải đầu ra JPEG vào một chương trình khác có thể thực hiện chuyển đổi sang PNG trước khi bạn đọc nó với ứng dụng thứ hai.

13

Đầu tiên: nếu bạn có đối tượng String thì nó không còn mã hóa nữa, đó là chuỗi Unicode thuần túy (*)!

Trong Java, mã hóa chỉ được sử dụng chỉ khi bạn chuyển đổi từ byte (byte[]) thành chuỗi (String) hoặc ngược lại. (Về mặt lý thuyết, bạn có thể thực hiện chuyển đổi trực tiếp từ byte[] thành byte[] nhưng tôi chưa thấy điều đó được thực hiện bằng Java).

Nếu bạn có một số dữ liệu CP1251 mã hóa, sau đó nó phải là một byte[] (ví dụ: một mảng byte) hoặc trong một số loại dòng (ví dụ cung cấp cho bạn như một InputStream).

Nếu bạn muốn cung cấp một số dữ liệu dưới dạng cp866, thì bạn phải cung cấp dữ liệu là byte[] hoặc dưới dạng một số loại luồng (ví dụ: `OutputStream).

Ngoài ra: không có nội dung nào là "utf8/cp1251". UTF-8 và CP-1251 là khá nhiều mã hóa ký tự không liên quan. Dữ liệu nhập của bạn là UTF-8 hoặc CP-1251 (hoặc cái gì khác). Nó không thể thực sự là cả hai (+).

Và đây là các liên kết bắt buộc: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

(*) có, nói đúng ra nó có một mã hóa và nó là UTF-16, nhưng đối với hầu hết các mục đích bạn có thể (và nên) nghĩ về nó như một "encodingless Chuỗi Unicode lý tưởng "
(+) nói đúng là có thể là cả hai nếu chỉ sử dụng ký tự mã hóa cùng một byte trong cả hai mã hóa, thường là tập con ASCII

+0

Tại sao thêm "Sigh ..." trong đó? Nó không thêm bất cứ điều gì vào câu trả lời và ngay từ cái nhìn đầu tiên nó có vẻ như nó đang coi thường OP. – McStretch

+0

Giải thích rất hay về những hiểu lầm phổ biến khác nhau về mã hóa ký tự wrt. Ước gì tôi có thể upvote nhiều hơn một lần. – sleske

+0

@McStretch: bạn nói đúng. Nó chỉ là phản ứng ban đầu của tôi khi tôi đọc những câu hỏi như thế. Tôi chỉ thêm phần còn lại của câu trả lời vì câu trả lời với * chỉ * "thở dài" sẽ thực sự khó chịu ;-) –

0

Vấn đề là, đầu ra bảng điều khiển của bạn không phải là cp866. Giao diện điều khiển là một, chuyển đổi là khác.

Chuỗi nội bộ trong java luôn là unicode, bộ ký tự rất quan trọng đối với các hoạt động đầu vào/đầu ra. Bạn đã không chỉ định những gì bạn muốn làm với chuỗi 'chuyển đổi', nhưng bạn chắc chắn sẽ thấy các lớp InputStreamReader/OutputStreamWriter. Chúng cung cấp các thiết lập charset cho các hoạt động I/O của bạn.

+3

Đó có thể là một vấn đề bổ sung, nhưng vấn đề cơ bản là anh ta đang hoạt động chuỗi mà không có ý nghĩa với nhau. Mã hóa một số văn bản như UTF-8 và giải mã nó bằng cách sử dụng một mã hóa khác không tạo ra một kết quả hữu ích. –

+0

tôi muốn gửi dữ liệu đến máy in tài chính hoạt động với DOS Cyrillic – NikolayGS

+2

Vì vậy OutputStreamWriter là những gì bạn cần. Ngay cả khi bạn cần byte [] để gửi trực tiếp đến cổng, bạn có thể hưởng lợi từ Writer ghi dữ liệu đến ByteArrayOutputStream. –

3

viết tắt:

Bạn giải mã một chuỗi utf8 như cp866. Vì utf8 và cp866 chỉ chia sẻ các ký hiệu ascii, mọi thứ khác bị xáo trộn.

Long:

Java đại diện cho Strings bằng cách sử dụng UTF-16 trong nội bộ, tất cả các đối tượng String được mã hóa dưới dạng UTF-16.

Charset.encode() tạo bộ mã hóa có chứa chuỗi trong mã hóa đã chọn, trong mã của bạn, điều này chuyển đổi chuỗi UTF-16 Java thành mảng byte được mã hóa utf-8.

Charset.decode() lấy mã dấu bytebuffer là Charset và chuyển đổi mã này thành chuỗi Java UTF-16. Trong trường hợp của bạn , bạn giải mã một chuỗi utf-8 với bộ giải mã cp866, dẫn đến một chuỗi bị xâu chuỗi.

Vì các chuỗi java có mã hóa được chỉ định, bạn phải chỉ định nó khi bạn đọc hoặc viết chúng. Cả InputStreamReader và OutputStreamWriter đều cung cấp các ctors với đối số Charset.

Dưới đây là ví dụ về cách bạn có thể chuyển đổi tệp/luồng.

//input the source is encoded in fromCharset 
BufferedReader in = new BufferedReader(new InputStreamReader(...,fromCharset)); 
//output the target will be encoded in toCharset 
PrintWriter out = new PrintWriter(new OutputStreamWriter(...,toCharset)); 
//reads a decoded String 
String line = in.readLine(); 
while(line != null) 
{ 
    out.println(line); 
    line = in.readLine(); 
} 
4

ngắn giải quyết cho vấn đề của bạn:

System.out.write("ВАСЯ\n".getBytes("cp866")); // its right 
System.out.println("ВАСЯ".getBytes("cp866")); // its wrong 

Kết quả từ cmd.exe:

C: \ Documents and Settings \ afram \ Мои документы \ NetBeansProjects \ Encoding \ quận> java -jar Encoding.jar

ВАСЯ

[B @ 1bab50a

+0

'System.out.write (" ВАСЯ \ n ".getBytes (" cp866 "))' là sai. Đầu ra là 'Р? Р? РЎРЇ' – Green

+1

@Green có thể do mã hóa của thiết bị đầu cuối của bạn khác với cp866. – vadipp

Các vấn đề liên quan