Chuyển đổi biểu tượng, chữ cái dấu sang bảng chữ cái tiếng Anh


129

Vấn đề là, như bạn biết, có hàng ngàn ký tự trong biểu đồ Unicode và tôi muốn chuyển đổi tất cả các ký tự tương tự thành các chữ cái trong bảng chữ cái tiếng Anh.

Ví dụ, đây là một vài chuyển đổi:

ҥ->H
Ѷ->V
Ȳ->Y
Ǭ->O
Ƈ->C
tђє Ŧค๓เℓy --> the Family
...

và tôi thấy rằng có hơn 20 phiên bản của chữ A / a. và tôi không biết cách phân loại chúng. Họ trông giống như kim trong đống cỏ khô.

Danh sách đầy đủ các ký tự unicode có tại http://www.ssec.wisc.edu/~tomw/java/unicode.html hoặc http://unicode.org/charts/charindex.html . Chỉ cần thử cuộn xuống và xem các biến thể của các chữ cái.

Làm cách nào tôi có thể chuyển đổi tất cả những thứ này bằng Java? Làm ơn giúp tôi :(


Xem câu hỏi này: stackoverflow.com/questions/249087/ Mạnh - cũng nên có một số câu hỏi khác về chủ đề này, nhưng tôi không thể tìm thấy chúng vào lúc này.
schnaader

1
Ví dụ thứ ba của bạn có nên Ȳ → Y không?
Dour High Arch

2
tại sao bạn muốn làm việc này? Nếu chúng tôi biết mục tiêu chung của bạn là gì, chúng tôi có thể có ích hơn.
David Thornley

David bạn biết một số EMO sử dụng các ký tự khác nhau trong câu. Ở đây bạn một ví dụ: ฬ.. t ฬ ¢ y <- Giải quyết điều này :) @schnaader, tôi nghĩ đó là những gì tôi đang tìm kiếm nhưng không phải trong Java.
AhmetB - Google

Cuộc trò chuyện này đã được thực hiện trước đây - xem @schnaader ở trên.
dkretz

Câu trả lời:


197

Đăng lại bài đăng của tôi từ Làm cách nào để xóa dấu phụ (dấu) khỏi chuỗi trong .NET?

Phương pháp này hoạt động tốt trong java (hoàn toàn cho mục đích loại bỏ các dấu phụ hay dấu phụ) .

Về cơ bản, nó chuyển đổi tất cả các ký tự có dấu thành các bản sao gốc của chúng, sau đó là dấu phụ kết hợp của chúng. Bây giờ bạn có thể sử dụng một biểu thức chính quy để loại bỏ dấu phụ.

import java.text.Normalizer;
import java.util.regex.Pattern;

public String deAccent(String str) {
    String nfdNormalizedString = Normalizer.normalize(str, Normalizer.Form.NFD); 
    Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
    return pattern.matcher(nfdNormalizedString).replaceAll("");
}

4
InCombiningDiacriticalMarks không chuyển đổi tất cả các loại thuốc nổ. Ví dụ: ARпштина, không được chạm tới. Sẽ thật tuyệt nếu người ta có thể chuyển đổi nó thành Opstina Bogomila hoặc một cái gì đó
iwein

13
Nó không phiên âm chút nào. Nó chỉ đơn thuần là loại bỏ các dấu phụ bị phân hủy ("dấu"). Bước trước đó (Form.NFD) phá vỡ á trong dấu + ', tức là phân tách ký tự có dấu thành ký tự không dấu cộng với dấu phụ. Điều này sẽ chuyển đổi cyrillic Ѽ thành Ѡ nhưng không hơn nữa.
MSalters

1
George đã đăng rằng có thể sử dụng tốt hơn \ p {IsM} thay vì \\ p {InCombiningDiacriticalMarks} tại glaforge.appspot.com/article/. Lưu ý rằng tôi chưa thử nghiệm nó.
ATorras

2
\\ p {IsM} dường như không hoạt động đối với các giọng Tây Ban Nha như á ó ú ñ é í. Ngược lại, "\\ p {InCombiningDiacriticalMarks} + đang hoạt động tốt cho việc này
Loic

Nó không hoạt động đối với tất cả các ký tự đặc biệt - Tôi đã gửi một vấn đề sai cho Android để biết rằng -> code.google.com/p/android/issues/detail?id=189515 Có ai biết cách chính xác để làm điều này không?
Michał Tajchert

71

Đây là một phần của Apache Commons Lang kể từ ver. 3.0.

org.apache.commons.lang3.StringUtils.stripAccents("Añ");

trả lại An

Đồng thời xem http://www.drillio.com/en/software-development/java/removing-accents-diacritics-in-any-lingu/


Giải pháp này thật tuyệt vời. Nó cũng hoạt động với tiếng Hy Lạp! Cảm ơn bạn.
Tom

5
Nó không hoàn hảo cho các ký tự tiếng Ba Lan dịch từ ł và Ł bị thiếu: đầu vào: ńŃÓŁĄĆĘŹąółęąćńŃ đầu ra: SZOŁACEZaołeacnN
Robert

1
Tiện ích tuyệt vời nhưng vì mã của nó giống hệt như mã được hiển thị trong câu trả lời được chấp nhận và bạn không muốn thêm phụ thuộc vào Commons Lang, bạn chỉ có thể sử dụng đoạn trích đã nói ở trên.
Polaretto

1
với apache thường gặp trong trường hợp của tôi: Đ không chuyển đổi sang D
Hoàng

@Hoang, Robert có thể có cơ hội gửi yêu cầu kéo :)
Ondra ižka

19

Cố gắng "chuyển đổi tất cả" là cách tiếp cận sai cho vấn đề.

Đầu tiên, bạn cần hiểu những hạn chế của những gì bạn đang cố gắng làm. Như những người khác đã chỉ ra, dấu phụ có một lý do: về cơ bản chúng là các chữ cái duy nhất trong bảng chữ cái của ngôn ngữ đó với ý nghĩa / âm thanh riêng của chúng, v.v .: loại bỏ các dấu này giống như thay thế các chữ cái ngẫu nhiên trong một từ tiếng Anh. Đây là trước khi bạn thậm chí xem xét các ngôn ngữ Cyrillic và các văn bản dựa trên chữ viết khác như tiếng Ả Rập, đơn giản là không thể "chuyển đổi" sang tiếng Anh.

Nếu bạn phải , vì bất kỳ lý do gì, chuyển đổi các ký tự, thì cách duy nhất hợp lý để tiếp cận nó là trước hết là giảm phạm vi của nhiệm vụ trong tay. Hãy xem xét nguồn của đầu vào - nếu bạn đang mã hóa một ứng dụng cho "thế giới phương Tây" (để sử dụng một cụm từ tốt như bất kỳ), có thể bạn sẽ không cần phải phân tích các ký tự tiếng Ả Rập. Tương tự, bộ ký tự Unicode chứa hàng trăm ký hiệu toán học và hình ảnh: không có cách nào (dễ dàng) để người dùng trực tiếp nhập chúng, vì vậy bạn có thể cho rằng chúng có thể bị bỏ qua.

Bằng cách thực hiện các bước hợp lý này, bạn có thể giảm số lượng ký tự có thể phân tích thành điểm mà hoạt động tra cứu / thay thế dựa trên từ điển là khả thi. Sau đó, nó trở thành một lượng nhỏ công việc hơi nhàm chán tạo ra từ điển và một nhiệm vụ tầm thường để thực hiện thay thế. Nếu ngôn ngữ của bạn hỗ trợ các ký tự Unicode gốc (như Java) và tối ưu hóa chính xác các cấu trúc tĩnh, thì việc tìm và thay thế đó có xu hướng nhanh chóng.

Điều này xuất phát từ kinh nghiệm làm việc trên một ứng dụng được yêu cầu cho phép người dùng cuối tìm kiếm dữ liệu thư mục có chứa các ký tự dấu phụ. Các mảng tra cứu (như trong trường hợp của chúng tôi) có lẽ phải mất 1 ngày để sản xuất, để bao gồm tất cả các dấu phụ cho tất cả các ngôn ngữ Tây Âu.


Tôi cảm ơn vì đã trả lời. Trên thực tế tôi không làm việc với các ngôn ngữ Ả Rập hoặc một cái gì đó tương tự. Bạn biết một số người sử dụng dấu phụ như những nhân vật ngộ nghĩnh và tôi phải loại bỏ nó nhiều nhất có thể. Ví dụ, tôi đã nói chuyển đổi "tђє Ŧ ค เ y -> Gia đình" trong ví dụ nhưng có vẻ khó chuyển đổi hoàn toàn. Tuy nhiên, chúng ta có thể thực hiện chuyển đổi "òéışöç-> oeisoc" một cách đơn giản. Nhưng cách chính xác để làm điều này là gì. Tạo mảng và thay thế bằng tay? Hay ngôn ngữ này có chức năng bản địa về vấn đề này?
AhmetB - Google

15

Vì mã hóa biến "Gia đình" thành "tђє Ŧ y" là ngẫu nhiên và không tuân theo bất kỳ thuật toán nào có thể được giải thích bằng thông tin của các mã điểm Unicode có liên quan, nên không có cách nào chung để giải quyết thuật toán này.

Bạn sẽ cần xây dựng ánh xạ các ký tự Unicode thành các ký tự Latin mà chúng giống nhau. Bạn có thể có thể làm điều này với một số máy học thông minh trên các glyph thực tế đại diện cho các điểm mã Unicode. Nhưng tôi nghĩ rằng nỗ lực cho việc này sẽ lớn hơn việc xây dựng bản đồ đó theo cách thủ công. Đặc biệt là nếu bạn có một số lượng lớn các ví dụ mà từ đó bạn có thể xây dựng bản đồ của mình.

Để làm rõ: một số thay thế thực sự có thể được giải quyết thông qua dữ liệu Unicode (như các câu trả lời khác chứng minh), nhưng một số chữ cái đơn giản là không có liên kết hợp lý với các ký tự Latin mà chúng giống nhau.

Ví dụ:

  • "ђ" (U + 0452 CYRILLIC SMALL LETTER DJE) có liên quan nhiều đến "d" hơn là "h", nhưng được sử dụng để đại diện cho "h".
  • "Ŧ" (U + 0166 LATIN VỐN THƯỞNG VỚI STROKE) có liên quan đến "T" (như tên cho thấy) nhưng được sử dụng để đại diện cho "F".
  • "ค" (U + 0E04 THAI CHARACTER KHO KHWAI) hoàn toàn không liên quan đến bất kỳ ký tự Latin nào và trong ví dụ của bạn được sử dụng để thể hiện "a"

7

Yêu cầu ban đầu đã được trả lời.

Tuy nhiên, tôi đang đăng câu trả lời dưới đây cho những người có thể đang tìm kiếm mã phiên âm chung để chuyển ngữ bất kỳ bộ ký tự sang tiếng Latin / tiếng Anh trong Java.

Ý nghĩa ngây thơ của phiên âm: Chuỗi được dịch ở dạng ký tự cuối cùng / ký tự đích nghe giống như chuỗi ở dạng ban đầu. Nếu chúng tôi muốn chuyển ngữ bất kỳ bộ ký tự sang tiếng Latin (bảng chữ cái tiếng Anh), thì ICU4 (thư viện ICU4J trong java) sẽ thực hiện công việc.

Đây là đoạn mã trong java:

    import com.ibm.icu.text.Transliterator; //ICU4J library import

    public static String TRANSLITERATE_ID = "NFD; Any-Latin; NFC";
    public static String NORMALIZE_ID = "NFD; [:Nonspacing Mark:] Remove; NFC";

    /**
    * Returns the transliterated string to convert any charset to latin.
    */
    public static String transliterate(String input) {
        Transliterator transliterator = Transliterator.getInstance(TRANSLITERATE_ID + "; " + NORMALIZE_ID);
        String result = transliterator.transliterate(input);
        return result;
    }

7

Chuỗi đã kiểm tra: ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏĐÑÒÓÔÕÖØÙÚÛÜÝß

Thử nghiệm :

  • Đầu ra từ Apache Commons Lang3 : AAAAAÆCEEEEIIIIĐNOOOOOØUUUUYß
  • Đầu ra từ ICU4j : AAAAAÆCEEEEIIIIĐNOOOOOØUUUUYß
  • Đầu ra từ JUnidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUUss (vấn đề với Ý và một vấn đề khác )
  • Đầu ra từ Unidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUYssss

Sự lựa chọn cuối cùng là tốt nhất.


1
@mehmet Chỉ cần theo dõi readme tại github.com/xuender/unidecode . Nó phải là một cái gì đó giống như Unidecode.decode ("ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏĐÑÒÓÔÕÖØÙÚÛÜÝß") sau khi nhập phụ thuộc.
xương rồng

6

Nếu nhu cầu là chuyển đổi "òéışöç-> oeisoc", bạn có thể sử dụng điểm này:

public class AsciiUtils {
    private static final String PLAIN_ASCII =
      "AaEeIiOoUu"    // grave
    + "AaEeIiOoUuYy"  // acute
    + "AaEeIiOoUuYy"  // circumflex
    + "AaOoNn"        // tilde
    + "AaEeIiOoUuYy"  // umlaut
    + "Aa"            // ring
    + "Cc"            // cedilla
    + "OoUu"          // double acute
    ;

    private static final String UNICODE =
     "\u00C0\u00E0\u00C8\u00E8\u00CC\u00EC\u00D2\u00F2\u00D9\u00F9"             
    + "\u00C1\u00E1\u00C9\u00E9\u00CD\u00ED\u00D3\u00F3\u00DA\u00FA\u00DD\u00FD" 
    + "\u00C2\u00E2\u00CA\u00EA\u00CE\u00EE\u00D4\u00F4\u00DB\u00FB\u0176\u0177" 
    + "\u00C3\u00E3\u00D5\u00F5\u00D1\u00F1"
    + "\u00C4\u00E4\u00CB\u00EB\u00CF\u00EF\u00D6\u00F6\u00DC\u00FC\u0178\u00FF" 
    + "\u00C5\u00E5"                                                             
    + "\u00C7\u00E7" 
    + "\u0150\u0151\u0170\u0171" 
    ;

    // private constructor, can't be instanciated!
    private AsciiUtils() { }

    // remove accentued from a string and replace with ascii equivalent
    public static String convertNonAscii(String s) {
       if (s == null) return null;
       StringBuilder sb = new StringBuilder();
       int n = s.length();
       for (int i = 0; i < n; i++) {
          char c = s.charAt(i);
          int pos = UNICODE.indexOf(c);
          if (pos > -1){
              sb.append(PLAIN_ASCII.charAt(pos));
          }
          else {
              sb.append(c);
          }
       }
       return sb.toString();
    }

    public static void main(String args[]) {
       String s = 
         "The result : È,É,Ê,Ë,Û,Ù,Ï,Î,À,Â,Ô,è,é,ê,ë,û,ù,ï,î,à,â,ô,ç";
       System.out.println(AsciiUtils.convertNonAscii(s));
       // output : 
       // The result : E,E,E,E,U,U,I,I,A,A,O,e,e,e,e,u,u,i,i,a,a,o,c
    }
}

JDK 1.6 cung cấp lớp java.text.N normalizer có thể được sử dụng cho nhiệm vụ này.

Xem một ví dụ ở đây


Thật không may, điều đó sẽ không xử lý các chữ số như.
Dour High Arch

Phương pháp này đặc biệt hữu ích nếu bạn cần phát hiện và xử lý các lớp dấu phụ khác nhau (nghĩa là thoát các ký tự đặc biệt trong LaTeX).
vallismortis

4

Bạn có thể thử sử dụng unidecode, có sẵn như một viên đá quý ruby và như một mô-đun perl trên cpan . Về cơ bản, nó hoạt động như một bảng tra cứu khổng lồ, trong đó mỗi điểm mã unicode liên quan đến một ký tự hoặc chuỗi ký tự ascii.


Bạn có thể có được một bảng tra cứu từ một trong số này.
Kathy Van Stone

Đây là một gói tuyệt vời, nhưng nó phiên âm âm thanh của nhân vật, ví dụ như nó chuyển đổi "" thành "Bei" bởi vì đó là âm thanh của nhân vật trong tiếng phổ thông. Tôi nghĩ rằng người hỏi muốn chuyển đổi glyphs thành những gì họ trông giống với tiếng Anh.
Dour High Arch

Nó làm điều đó cho các nhân vật Latin, mặc dù. â trở thành a, et al. @ahmetalpbalkan Tôi đồng ý với Kathy, bạn có thể sử dụng nó làm tài nguyên để xây dựng bảng tra cứu của riêng bạn, logic nên khá đơn giản. Không may là dường như không có phiên bản java.
Daniel Vandersluis

@ahmetalpbalkan Đây là unidecode cho Java.
Jakub Jiruska

4

Không có cách dễ dàng hoặc chung chung để làm những gì bạn muốn bởi vì đó chỉ là ý kiến ​​chủ quan của bạn rằng những chữ cái này trông giống như các chữ cái Latin mà bạn muốn chuyển đổi. Chúng thực sự là những chữ cái riêng biệt với tên và âm thanh riêng biệt của chúng, trông giống như một chữ cái Latinh.

Nếu bạn muốn chuyển đổi đó, bạn phải tạo bảng dịch của riêng mình dựa trên những chữ cái Latinh nào bạn nghĩ rằng các chữ cái không phải là chữ Latinh nên được chuyển đổi thành.

(Nếu bạn chỉ muốn xóa dấu phụ, có một số câu trả lời trong chuỗi này: Làm cách nào để xóa dấu phụ (dấu) khỏi chuỗi trong .NET? Tuy nhiên, bạn mô tả một vấn đề chung hơn)


+1. Đây là phiên bản Java của câu hỏi 'loại bỏ dấu phụ: stackoverflow.com/questions/1016955/ mẹo ; xem câu trả lời của Michael Borgwardt và devio
Jonik

4

Tôi đến bữa tiệc muộn, nhưng sau khi đối mặt với vấn đề này ngày hôm nay, tôi thấy câu trả lời này rất hay:

String asciiName = Normalizer.normalize(unicodeName, Normalizer.Form.NFD)
    .replaceAll("[^\\p{ASCII}]", "");

Tham khảo: https://stackoverflow.com/a/16283863


Cảnh báo nhỏ - nó loại bỏ U + 00DF LATIN SMALL LETTER SHARP S "ß"
rafalmag

Và cũng ... Thật tệ.
xương rồng

4

Vấn đề với việc "chuyển đổi" Unicode tùy ý sang ASCII là ý nghĩa của một ký tự phụ thuộc vào văn hóa. Ví dụ, chuyển đổi tiếng Anh thành một người nói tiếng Đức nên được chuyển đổi thành "ss" trong khi một người nói tiếng Anh có thể sẽ chuyển đổi nó thành Hồi Bặt.

Thêm vào đó, thực tế là Unicode có nhiều điểm mã cho cùng một glyphs.

Kết quả cuối cùng là cách duy nhất để làm điều này là tạo một bảng lớn với mỗi ký tự Unicode và ký tự ASCII mà bạn muốn chuyển đổi thành. Bạn có thể đi đường tắt bằng cách chuẩn hóa các ký tự có dấu sang dạng chuẩn hóa KD, nhưng không phải tất cả các ký tự đều chuẩn hóa thành ASCII. Ngoài ra, Unicode không xác định phần nào của glyph là "dấu".

Đây là một đoạn trích nhỏ từ một ứng dụng thực hiện điều này:

switch (c)
{
    case 'A':
    case '\u00C0':  //  À LATIN CAPITAL LETTER A WITH GRAVE
    case '\u00C1':  //  Á LATIN CAPITAL LETTER A WITH ACUTE
    case '\u00C2':  //  Â LATIN CAPITAL LETTER A WITH CIRCUMFLEX
    // and so on for about 20 lines...
        return "A";
        break;

    case '\u00C6'://  Æ LATIN CAPITAL LIGATURE AE
        return "AE";
        break;

    // And so on for pages...
}

Tôi đồng ý. Bạn nên tạo một từ điển chuyển đổi cụ thể cho ứng dụng của bạn và đối tượng mong đợi. Ví dụ: đối với khán giả nói tiếng Tây Ban Nha, tôi sẽ chỉ dịch ÁÉÍÓÚÜÑáéíóúü¿¡
Roberto Bonvallet

Roberto có hàng ngàn nhân vật và tôi không thể làm hướng dẫn này.
AhmetB - Google

2
Ngôn ngữ của con người bạn đang sử dụng có "hàng ngàn" ký tự? Tiếng Nhật? Bạn mong đợi điều gì ど う し よ と し て い sẽ được chuyển đổi thành?
Dour High Arch

6
Ví dụ bạn đã đưa ra không lý tưởng: U + 00DF LATIN SMALL LETTER SHARP S "ß" không phải là chữ cái Unicode giống như U + 03B2 GREEK SMALL LALLTER BETA "".
Joachim Sauer

2

Theo lớp thực hiện thủ thuật:

org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter
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.