2009-08-13 33 views
5

Tôi đang cố gắng giải mã một số chuỗi UTF-8 trong Java. Những chuỗi này chứa một số ký tự unicode kết hợp, chẳng hạn như CC 88 (kết hợp diaresis). Chuỗi ký tự có vẻ ok, theo http://www.fileformat.info/info/unicode/char/0308/index.htmJava UTF-8 hành vi lạ

Nhưng đầu ra sau khi chuyển đổi thành Chuỗi không hợp lệ. Bất kỳ ý tưởng nào?

byte[] utf8 = { 105, -52, -120 }; 
System.out.print("{{"); 
for(int i = 0; i < utf8.length; ++i) 
{ 
    int value = utf8[i] & 0xFF; 
    System.out.print(Integer.toHexString(value)); 
} 
System.out.println("}}"); 
System.out.println(">" + new String(utf8, "UTF-8")); 

Output:

 
    {{69cc88}} 
    >i? 

Trả lời

9

Giao diện điều khiển mà bạn đang outputting đến (ví dụ cửa sổ) không thể hỗ trợ unicode, và có thể mangle ký tự. Đầu ra bàn điều khiển không phải là biểu diễn tốt của dữ liệu.

Hãy thử viết đầu ra vào một tệp thay thế, đảm bảo mã hóa đúng trên FileWriter, sau đó mở tệp trong trình chỉnh sửa thân thiện với unicode.

Cách khác, sử dụng trình gỡ lỗi để đảm bảo các ký tự là những gì bạn mong đợi. Chỉ cần không tin tưởng giao diện điều khiển.

+0

+1: Trên Ubuntu 9,04 trong thiết bị đầu cuối (gnome-terminal) đầu ra là i với diaresis như bạn có thể mong đợi nó. –

+0

Tôi thích từ "diaeresis" này. Tôi có thể phải sử dụng nó thường xuyên hơn trong cuộc trò chuyện. – skaffman

+1

:) cũng thử "umlaut", và bạn sẽ là người đàn ông của buổi tối. –

4

Mã này là tốt, nhưng như skaffman nói giao diện điều khiển của bạn có lẽ không hỗ trợ các ký tự thích hợp.

Để kiểm tra cho chắc chắn, bạn cần phải in ra các giá trị unicode của ký tự:

public class Test { 
    public static void main(String[] args) throws Exception { 
     byte[] utf8 = { 105, -52, -120 }; 
     String text = new String(utf8, "UTF-8"); 
     for (int i=0; i < text.length(); i++) { 
      System.out.println(Integer.toHexString(text.charAt(i))); 
     } 
    } 
} 

này in 69, 308 - đó là correct (U + 0069, U + 0308).

4

Cả hai đều đúng. Cảm ơn !!

đây như thế nào cuối cùng tôi giải quyết được vấn đề, trong Eclipse trên Windows:

  • Trong Configuration Run, tab Arguments, tôi đã thêm "-Dfile.encoding=UTF-8" để các đối số VM
  • Trong cấu hình Run, tab Common , tôi đặt Console Encoding là UTF-8

Và tôi sửa đổi mã như sau:

byte[] utf8 = { 105, -52, -120 }; 
System.out.print("{{"); 
for(int i = 0; i < utf8.length; ++i) 
{ 
    int value = utf8[i] & 0xFF; 
    System.out.print(Integer.toHexString(value)); 
} 
System.out.println("}}"); 

PrintStream sysout = new PrintStream(System.out, true, "UTF-8"); 
sysout.print(">" + new String(utf8, "UTF-8")); 

Đầu ra:

 
{{69cc88}} 
> ï 

Cảm ơn!

+0

Bạn không cần chuyển đổi "-Dfile.encoding = UTF-8" nếu bạn định tự mã hóa dữ liệu bằng PrintStream. (Đặt thủ công thuộc tính "file.encoding" có thể có vấn đề đối với bất kỳ mã nào cần biết mã hóa hệ thống.) – McDowell

1

Java, không phải là không hợp lý, mã hóa các ký tự Unicode thành byte được mã hóa hệ thống gốc trước khi ghi chúng vào stdout. Một số hệ điều hành, giống như nhiều bản phân phối Linux, sử dụng UTF-8 làm bộ ký tự mặc định của chúng, điều này rất hay.

Mọi thứ có một chút khác biệt trên Windows vì nhiều lý do tương thích ngược. Mã hóa hệ thống mặc định sẽ là một trong các mã "ANSI" và nếu bạn mở dấu nhắc lệnh mặc định (cmd.exe), nó sẽ là một trong các mã "DOS" OEM "cũ (mặc dù có thể nhận ANSI và Unicode tại đó with a bit of work).

Vì U + 0308 không nằm trong bất kỳ bộ ký tự "ANSI" nào (có thể là 1252 trong trường hợp của bạn), nó sẽ được mã hóa dưới dạng ký tự lỗi (thường là dấu chấm hỏi).

Một thay thế cho Unicode cho phép tất cả mọi thứ là để normalize chuỗi kết hợp U + 0069 U + 0308 đến ký tự đơn U + 00EF:

public static void emit(String foo) throws IOException { 
    System.out.println("Literal: " + foo); 
    System.out.print("Hex: "); 
    for (char ch : foo.toCharArray()) { 
     System.out.print(Integer.toHexString(ch & 0xFFFF) + " "); 
    } 
    System.out.println(); 
    } 

    public static void main(String[] args) throws IOException { 
    String foo = "\u0069\u0308"; 
    emit(foo); 
    foo = Normalizer.normalize(foo, Normalizer.Form.NFC); 
    emit(foo); 
    } 

Dưới windows-1252, mã này sẽ phát ra:

 
Literal: i? 
Hex: 69 308 
Literal: ï 
Hex: ef