2010-01-19 39 views
8

Cách tốt nhất để chuyển đổi chuỗi từ Unicode thành ASCII mà không thay đổi độ dài của nó (điều đó rất quan trọng trong trường hợp của tôi)? Ngoài ra, các ký tự không có bất kỳ sự cố chuyển đổi nào cũng phải ở cùng vị trí như trong chuỗi gốc. Vì vậy, một "Ä" phải được chuyển đổi thành "A" và không phải thứ gì đó bí ẩn có nhiều ký tự hơn.Chuyển đổi Unicode sang ASCII mà không thay đổi độ dài chuỗi (bằng Java)

Chỉnh sửa:
@novalis - Ký hiệu như vậy (ví dụ ngôn ngữ Châu Á) chỉ nên được chuyển đổi thành một số trình giữ chỗ. Tôi không quá quan tâm đến những từ đó hay ý nghĩa của chúng.

@MtnViewMark - Tôi phải bảo toàn số lượng ký tự và vị trí của các ký tự có sẵn ASCII trong mọi trường hợp.

Dưới đây là một số thông tin khác: Tôi có một số công cụ khai phá văn bản chỉ có thể xử lý chuỗi ASCII. Hầu hết văn bản cần được xử lý bằng tiếng Anh, nhưng một số văn bản có chứa các ký tự không phải ASCII. Tôi không quan tâm đến những từ đó, nhưng tôi phải chắc chắn rằng những từ tôi quan tâm (những từ chỉ chứa các ký tự ASCII) ở cùng vị trí sau khi chuyển đổi chuỗi.

+5

Bạn định chuyển đổi 口水 雞 thành gì? Tôi không biết làm thế nào người ta có thể diễn tả khái niệm về gà nước bọt trong ba nhân vật ascii. – novalis

+0

Nó không rõ ràng - là bạn đang cố gắng để bảo vệ số lượng ký tự hoặc số byte ... hoặc có lẽ chiều rộng của chuỗi khi được hiển thị? – MtnViewMark

+0

@novalis +1 cho gà nước bọt :-) –

Trả lời

12

Như đã nêu trong this câu trả lời, đoạn code sau sẽ làm việc:

String s = "口水雞 hello Ä"; 

    String s1 = Normalizer.normalize(s, Normalizer.Form.NFKD); 
    String regex = "[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+"; 

    String s2 = new String(s1.replaceAll(regex, "").getBytes("ascii"), "ascii"); 

    System.out.println(s2); 
    System.out.println(s.length() == s2.length()); 

Output là

??? hello A 
true 

Vì vậy, đầu tiên bạn loại bỏ các vết diactrical, sự chuyển đổi để ascii. Ký tự không phải ascii sẽ trở thành dấu hỏi.

+0

Cảm ơn ... dường như hoạt động gần như tốt. Nhưng có một vấn đề với ký tự '^'. Khi nó ở trong một chuỗi (như "he ^^ o") nó không thành công (chỉ đơn giản là bị xóa). – Zardoz

+0

Chỉ cần xóa \\ p {IsLm} \\ p {IsSk} khỏi regex. –

+1

Nếu bất cứ ai muốn loại bỏ dấu hỏi và giảm đầy đủ các văn bản để chữ cơ bản thử: "[\\ P {InBasicLatin}] +" (chú ý chữ hoa-P có nghĩa là "Không có trong) Tested sử dụng:. Rrrr̈r'ŕřttẗţỳỹẙy'yýÿŷpp̈sss̈s̊s's̸śŝŞşšddd̈ďd'ḑf̈f̸ggg̈g'ģq ĝǧḧĥj̈j'ḱkk̈k̸ǩlll̈Łłẅẍcc̈c̊c'c̸Çççćĉčvv̈v'v̸bb̧ǹnn̈n̊n'ńņňñmmmm̈ m̊m̌ǵß – RedYeti

7

Sử dụng java.text.Normalizer.normalize() với Normalizer.Form.NFD, sau đó lọc ra các ký tự không phải ASCII.

+0

Đây có lẽ là những gì Zardoz thực sự muốn, mặc dù nó sẽ không hiệu quả đối với các nhân vật không có trong các trang tiếng Latin. –

+0

+1 điều này giống như giải pháp tốt nhất cho vấn đề (theo như có thể được kể từ câu hỏi). –

+0

Unicode chuẩn hóa sẽ chỉ hoạt động đối với các ký tự, có thể bao gồm ký tự latin thuần từ bộ ký tự ASCII và dấu dấu phụ. – jarnbjo

2

Lưu ý: Tôi không biết Java. Chỉ một chút về bộ ký tự.

Bạn không nêu rõ bộ ký tự nào bạn đang sử dụng chính xác.

Nhưng không có vấn đề mà bạn sử dụng, nó không thể chuyển đổi một chuỗi Unicode để ASCII giữ độ dài và tính cách vị trí ban đầu, đơn giản chỉ vì một bộ ký tự Unicode sẽ sử dụng nhiều byte cho một số nhân vật (rõ ràng).

Ngoại lệ duy nhất tôi biết sẽ là chuỗi UTF-8 chỉ chứa ký tự ASCII: Chuỗi này sẽ giống hệt nhau ở cả UTF-8 và ASCII, vì UTF-8 chỉ sử dụng các ký tự nhiều byte khi cần. (Tôi không biết về các hương vị Unicode khác, có thể có những năng động khác).

Cách giải quyết duy nhất tôi có thể thấy là thêm khoảng trắng vào bất kỳ ký tự đặc biệt nào được thay thế bằng ký tự ASCII, nhưng điều đó sẽ làm hỏng chuỗi (Göteborg trong UTF8 sẽ phải trở thành Go teborg để giữ độ dài).

Có thể bạn muốn xây dựng dựa trên những gì bạn muốn/cần đạt được, vì vậy mọi người ở đây có thể đề xuất cách giải quyết.

+0

Java sử dụng UTF-16 cho các chuỗi nội bộ, vì vậy đối với hầu hết các ngôn ngữ "phương Tây" phổ biến, văn bản gốc và văn bản "ASCII-giảm" sẽ có cùng độ dài (lưu dấu chấm câu không thường xuyên). –

2

Một vấn đề với Normalizer là trước Java 1.6 của nó trong gói sun.text trong khi trong 1.6 của nó trong gói java.text và chữ ký phương thức đã thay đổi. Vì vậy, nếu ứng dụng của bạn neeeds để chạy trên cả hai nền tảng, bạn sẽ phải sử dụng sự phản ánh.

Một giải pháp tùy chỉnh thay thế được mô tả như techniwue 3 here

2

Như Paul Taylor nói: có vấn đề với việc sử dụng Normalizer nếu bạn cần dự án để có thể biên dịch/Runnable trong pre-1.6 và cũng có trong 1.6 và java cao . Bạn sẽ gặp rắc rối vì Normalizer nằm trong các gói khác nhau (java.text.Normalizer (đối với 1.6) thay vì sun.text.Normalizer (đối với trước 1.6)) và có chữ ký phương thức khác.

Thông thường, bạn nên sử dụng sự phản chiếu để gọi phương thức Normalizer.normalize() phù hợp. (Example could be found here).
Nhưng nếu bạn không muốn đặt sự lộn xộn phản chiếu trong mã của mình, bạn có thể sử dụng icu4j library. Nó chứa com.ibm.icu.text.Normalizer lớp học với phương thức normalize() thực hiện cùng một công việc như java.text.Normalizer/sun.text.Normalizer. Icu thư viện có (nên có) thực hiện riêng của Normalizer để bạn có thể chia sẻ dự án của bạn với thư viện và đó nên được độc lập java.
Bất lợi là thư viện icu khá lớn.

Nếu bạn sử dụng lớp Normalizer chỉ để xóa dấu trọng âm/dấu phụ khỏi chuỗi, cũng có cách khác. Bạn có thể sử dụng Apache commons lang library (ver. 3) chứa StringUtils với phương pháp stripAccents():

String noAccentsString = org.apache.commons.lang3.StringUtils.stripAccents(s); 

thư viện Lang3 lẽ sử dụng phản ánh để gọi Normalizer thích hợp theo phiên bản java. Vì vậy, lợi thế là bạn không có sự lộn xộn phản chiếu trong mã của bạn.

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