การแปลงสัญลักษณ์ตัวอักษรเน้นเสียงเป็นตัวอักษรภาษาอังกฤษ


129

ปัญหาคืออย่างที่คุณทราบมีอักขระหลายพันตัวในแผนภูมิ Unicodeและฉันต้องการแปลงอักขระที่คล้ายกันทั้งหมดเป็นตัวอักษรที่เป็นตัวอักษรภาษาอังกฤษ

ตัวอย่างเช่นนี่คือการแปลงบางส่วน:

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

และฉันเห็นว่ามีตัวอักษร A / a มากกว่า 20 เวอร์ชัน และฉันไม่รู้ว่าจะจัดประเภทอย่างไร ดูเหมือนเข็มในกองหญ้า

รายการที่สมบูรณ์ของตัวอักษร Unicode ที่http://www.ssec.wisc.edu/~tomw/java/unicode.html หรือhttp://unicode.org/charts/charindex.html เพียงแค่ลองเลื่อนลงและดูรูปแบบของตัวอักษร

ฉันจะแปลงสิ่งเหล่านี้ด้วย Java ได้อย่างไร โปรดช่วยฉัน :(


ดูคำถามนี้: stackoverflow.com/questions/249087/… - นอกจากนี้ยังควรมีคำถามอื่น ๆ เกี่ยวกับหัวข้อนี้ แต่ฉันไม่พบคำถามในขณะนี้
schnaader

1
ตัวอย่างที่สามของคุณควรเป็นȲ→ Y หรือไม่?
Dour High Arch

2
ทำไมคุณถึงต้องการทำเช่นนี้? หากเรารู้ว่าเป้าหมายโดยรวมของคุณคืออะไรเราอาจช่วยได้มากขึ้น
David Thornley

เดวิดคุณรู้จัก EMO บางตัวใช้ตัวอักษรที่แตกต่างกันในประโยค นี่คือตัวอย่าง: ฬ. ¢. tђєฬเยη∂єг¢คקђŦค ๓ เ y <- แก้ปัญหานี้ :) @schnaader ฉันคิดว่านั่นคือสิ่งที่ฉันกำลังมองหา แต่ไม่ใช่ใน Java
AhmetB - Google

การสนทนานี้เคยทำมาก่อนแล้วโปรดดู @schnaader ด้านบน
dkretz

คำตอบ:


197

การโพสต์โพสต์ของฉันใหม่จากฉันจะลบตัวกำกับเสียง (สำเนียง) จากสตริงใน. NET ได้อย่างไร

วิธีนี้ใช้ได้ผลดีใน java (เพื่อจุดประสงค์ในการลบเครื่องหมายกำกับเสียงหรือสำเนียงเท่านั้น)

โดยทั่วไปจะแปลงอักขระที่เน้นเสียงทั้งหมดให้เป็นคู่ของ deAccented ตามด้วยการกำกับเสียง ตอนนี้คุณสามารถใช้ regex เพื่อตัดเครื่องหมายกำกับเสียงออก

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 ไม่ได้แปลงซิริลลิกทั้งหมด ตัวอย่างเช่นОпштинаБогомилаไม่ถูกแตะต้อง คงจะดีไม่น้อยถ้าใครสามารถแปลงเป็น Opstina Bogomila หรืออะไรก็ได้
iwein

13
มันไม่ทับศัพท์เลย เป็นเพียงการลบเครื่องหมายกำกับเสียงที่สลายตัวแล้ว ("สำเนียง") ขั้นตอนก่อนหน้า (Form.NFD) แบ่งáใน a + 'นั่นคือการย่อยสลายอักขระที่เน้นเสียงเป็นอักขระที่ไม่มีการเน้นเสียงบวกกับเครื่องหมายกำกับเสียง สิ่งนี้จะแปลงซิริลลิกѼเป็นѠ แต่ไม่ต่อไป
MSalters

1
จอร์จโพสต์ว่าควรใช้ \\ p {IsM} แทน \\ p {InCombiningDiacriticalMarks} ที่glaforge.appspot.com/article/…โปรดทราบว่าฉันยังไม่ได้ทดสอบ
ATorras

2
\\ p {IsM} ดูเหมือนจะใช้ไม่ได้กับสำเนียงภาษาสเปนเช่นáóúñéí ตรงกันข้าม "\\ p {InCombiningDiacriticalMarks} + ทำงานได้ดีสำหรับเรื่องนี้
Loic

มันใช้ไม่ได้กับอักขระพิเศษทั้งหมด - ฉันส่งปัญหาที่ไม่ถูกต้องสำหรับ Android เพื่อเรียนรู้ว่า -> code.google.com/p/android/issues/detail?id=189515ใครรู้วิธีที่ถูกต้องในการทำเช่นนี้
Michał Tajchert

71

เป็นส่วนหนึ่งของApache Commons Langในเวอร์ชัน 3.0

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

ผลตอบแทน An

โปรดดูที่http://www.drillio.com/en/software-development/java/removing-accents-diacritics-in-any-language/


การแก้ปัญหานี้น่าทึ่งมาก มันใช้ได้กับกรีกด้วย! ขอบคุณ.
ทอม

5
ไม่เหมาะสำหรับการแปลตัวอักษรโปแลนด์จากłและ missing ขาดหายไป: input: ŚŻÓŁĄĆĘŹąółęąćńŃ output: SZOŁACEZaołeacnN
Robert

1
ยูทิลิตี้ที่ดี แต่เนื่องจากรหัสของมันเหมือนกับที่แสดงในคำตอบที่ยอมรับและคุณไม่ต้องการเพิ่มการพึ่งพา Commons Lang คุณสามารถใช้ข้อมูลโค้ดดังกล่าวได้
polaretto

1
กับ apache ทั่วไปในกรณีของฉัน: Đไม่แปลงเป็น D
Hoang

@Hoang โรเบิร์ตอาจมีโอกาสส่งคำขอดึง :)
Ondra Žižka

19

การพยายาม "แปลงทั้งหมด" เป็นแนวทางที่ไม่ถูกต้องในการแก้ปัญหา

ประการแรกคุณต้องเข้าใจข้อ จำกัด ของสิ่งที่คุณพยายามทำ ดังที่คนอื่น ๆ ได้ชี้ให้เห็นการออกเสียงมีเหตุผล: โดยพื้นฐานแล้วพวกมันเป็นตัวอักษรที่ไม่ซ้ำกันในตัวอักษรของภาษานั้นโดยมีความหมาย / เสียงของตัวเอง ฯลฯ การลบเครื่องหมายเหล่านั้นก็เหมือนกับการแทนที่ตัวอักษรแบบสุ่มในคำภาษาอังกฤษ ก่อนที่คุณจะพิจารณาภาษาซีริลลิกและข้อความตามสคริปต์อื่น ๆ เช่นภาษาอาหรับซึ่งไม่สามารถ "แปลง" เป็นภาษาอังกฤษได้

หากคุณต้องแปลงอักขระด้วยเหตุผลใดก็ตามวิธีเดียวที่เหมาะสมในการเข้าถึงสิ่งนี้เพื่อลดขอบเขตของงานในมือก่อน พิจารณาแหล่งที่มาของข้อมูลที่ป้อน - หากคุณกำลังเขียนโค้ดแอปพลิเคชันสำหรับ "โลกตะวันตก" (เพื่อใช้เป็นวลีที่ดีเช่นใด ๆ ) ก็ไม่น่าเป็นไปได้ที่คุณจะต้องแยกวิเคราะห์อักขระภาษาอาหรับ ในทำนองเดียวกันชุดอักขระ Unicode มีสัญลักษณ์ทางคณิตศาสตร์และรูปภาพหลายร้อยสัญลักษณ์: ไม่มีวิธี (ง่าย) สำหรับผู้ใช้ในการป้อนสิ่งเหล่านี้โดยตรงดังนั้นคุณสามารถสันนิษฐานได้ว่าสามารถละเว้นได้

ด้วยการทำตามขั้นตอนเชิงตรรกะเหล่านี้คุณสามารถลดจำนวนอักขระที่เป็นไปได้เพื่อแยกวิเคราะห์จนถึงจุดที่สามารถดำเนินการค้นหา / แทนที่ตามพจนานุกรมได้ จากนั้นมันจะกลายเป็นงานที่น่าเบื่อเล็กน้อยในการสร้างพจนานุกรมและเป็นงานเล็กน้อยที่ต้องดำเนินการแทนที่ หากภาษาของคุณรองรับอักขระ Unicode ดั้งเดิม (เช่นเดียวกับ Java) และปรับโครงสร้างแบบคงที่อย่างถูกต้องการค้นหาและแทนที่ดังกล่าวมักจะรวดเร็วอย่างไม่น่าเชื่อ

สิ่งนี้มาจากประสบการณ์ในการทำงานกับแอปพลิเคชันที่จำเป็นเพื่อให้ผู้ใช้สามารถค้นหาข้อมูลบรรณานุกรมที่มีอักขระกำกับเสียง อาร์เรย์การค้นหา (เช่นเดียวกับในกรณีของเรา) อาจใช้เวลา 1 วันในการผลิตเพื่อให้ครอบคลุมเครื่องหมายกำกับเสียงสำหรับภาษายุโรปตะวันตกทั้งหมด


ขอบคุณสำหรับคำตอบ อันที่จริงฉันไม่ได้ทำงานกับภาษาอาหรับหรืออะไรทำนองนั้น คุณรู้ไหมว่าบางคนใช้ตัวกำกับเสียงเป็นตัวตลกและฉันต้องลบมันออกให้มากที่สุดเท่าที่จะทำได้ ตัวอย่างเช่นฉันพูดว่าการแปลง "tђєŦค ๓ เ y -> ครอบครัว" ในตัวอย่าง แต่ดูเหมือนว่าจะแปลงให้สมบูรณ์ได้ยาก อย่างไรก็ตามเราสามารถทำการแปลง "òéışöç-> oeisoc" ได้ด้วยวิธีง่ายๆ แต่วิธีที่แน่นอนในการทำเช่นนี้ การสร้างอาร์เรย์และการแทนที่ด้วยตนเอง? หรือภาษานี้มีฟังก์ชันดั้งเดิมเกี่ยวกับปัญหานี้หรือไม่
AhmetB - Google

15

เนื่องจากการเข้ารหัสที่เปลี่ยน "ครอบครัว" เป็น "tђєŦค ๓ เℓy" เป็นแบบสุ่มอย่างมีประสิทธิภาพและไม่เป็นไปตามอัลกอริทึมใด ๆ ที่สามารถอธิบายได้จากข้อมูลของจุดรหัส Unicode ที่เกี่ยวข้องจึงไม่มีวิธีทั่วไปในการแก้อัลกอริทึมนี้

คุณจะต้องสร้างการแมปอักขระ Unicode เป็นอักขระละตินที่มีลักษณะคล้ายกัน คุณอาจทำได้ด้วยการเรียนรู้ของเครื่องอัจฉริยะบนร่ายมนตร์จริงที่แสดงถึงจุดรหัส Unicode แต่ฉันคิดว่าความพยายามนี้น่าจะมากกว่าการสร้างแผนที่ด้วยตนเอง โดยเฉพาะอย่างยิ่งถ้าคุณมีตัวอย่างจำนวนมากที่คุณสามารถสร้างแผนที่ของคุณได้

เพื่อชี้แจง: การแทนที่บางส่วนสามารถแก้ไขได้จริงผ่านข้อมูล Unicode (ตามที่คำตอบอื่น ๆ แสดงให้เห็น) แต่ตัวอักษรบางตัวไม่มีความสัมพันธ์ที่สมเหตุสมผลกับอักขระละตินที่มีลักษณะคล้ายกัน

ตัวอย่าง:

  • "ђ" (U + 0452 CYRILLIC SMALL LETTER DJE) เกี่ยวข้องกับ "d" มากกว่า "h" แต่ใช้แทน "h"
  • "Ŧ" (U + 0166 LATIN CAPITAL LETTER T WITH STROKE) ค่อนข้างเกี่ยวข้องกับ "T" (ตามชื่อ) แต่ใช้แทน "F"
  • "ค" (U + 0E04 THAI CHARACTER KHO KHWAI) ไม่เกี่ยวข้องกับอักขระละตินใด ๆ เลยและในตัวอย่างของคุณใช้แทน "a"

7

คำขอเดิมได้รับคำตอบแล้ว

อย่างไรก็ตามฉันกำลังโพสต์คำตอบด้านล่างสำหรับผู้ที่อาจกำลังมองหารหัสทับศัพท์ทั่วไปเพื่อทับศัพท์ชุดอักขระเป็นภาษาละติน / อังกฤษใน Java

ความหมายที่ไร้เดียงสาของ tranliteration: สตริงที่แปลในรูปแบบสุดท้าย / ชุดอักขระเป้าหมายฟังดูเหมือนสตริงในรูปแบบดั้งเดิม หากเราต้องการทับศัพท์ชุดอักขระเป็นภาษาละติน (ตัวอักษรภาษาอังกฤษ) ICU4 (ไลบรารี ICU4J ใน java) จะทำงาน

นี่คือข้อมูลโค้ดใน 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

ทดสอบสตริง: ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝß

ทดสอบแล้ว:

  • เอาต์พุตจาก Apache Commons Lang3 : AAAAAÆCEEEEIIIIÐNOOOOOØUUUUYß
  • เอาต์พุตจาก ICU4j : AAAAAÆCEEEEIIIIÐNOOOOOØUUUUYß
  • เอาต์พุตจากJUnidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUUss (ปัญหาเกี่ยวกับÝและอื่น ๆปัญหา )
  • เอาต์พุตจากUnidecode : AAAAAAECEEEEIIIIDNOOOOOOUUUUYss

ทางเลือกสุดท้ายคือสิ่งที่ดีที่สุด


1
@mehmet เพียงทำตาม README ที่github.com/xuender/unidecode ควรจะเป็น Unidecode.decode ("ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝß") หลังจากอิมพอร์ตการอ้างอิง
cactuschibre

6

หากจำเป็นต้องแปลง "òéışöç-> oeisoc" คุณสามารถใช้จุดเริ่มต้นนี้:

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 จัดเตรียมคลาส java.text.Normalizer ที่สามารถใช้สำหรับงานนี้

ดูตัวอย่างได้ที่นี่


น่าเสียดายที่ไม่สามารถจัดการกับ ligatures เช่นÆ
Dour High Arch

วิธีนี้มีประโยชน์อย่างยิ่งหากคุณต้องการตรวจจับและจัดการคลาสของการกำกับเสียงที่แตกต่างกัน (เช่นการใช้อักขระพิเศษใน LaTeX)
vallismortis

4

คุณอาจลองใช้unidecodeซึ่งสามารถใช้ได้เป็นอัญมณีทับทิมและเป็นโมดูล Perl บน cpan โดยพื้นฐานแล้วมันทำงานเป็นตารางการค้นหาขนาดใหญ่โดยที่แต่ละจุดรหัส Unicode เกี่ยวข้องกับอักขระหรือสตริง ascii


คุณอาจได้รับตารางการค้นหาจากหนึ่งในตารางเหล่านี้
Kathy Van Stone

นี่เป็นแพ็กเกจที่น่าทึ่ง แต่มันทับศัพท์เสียงของตัวละครเช่นจะแปลง "北" เป็น "Bei" เพราะนั่นคือสิ่งที่ตัวละครฟังดูเหมือนในภาษาจีนกลาง ฉันคิดว่าผู้ถามต้องการแปลงร่ายมนตร์เป็นสิ่งที่เห็นเป็นภาษาอังกฤษ
Dour High Arch

มันทำเช่นนั้นสำหรับตัวอักษรละตินแม้ว่า กลายเป็น a, et al. @ahmetalpbalkan ฉันเห็นด้วยกับ Kathy คุณสามารถใช้มันเป็นทรัพยากรในการสร้างตารางการค้นหาของคุณเองตรรกะควรจะค่อนข้างง่าย น่าเสียดายที่ดูเหมือนว่าจะไม่มีเวอร์ชัน java
Daniel Vandersluis

@ahmetalpbalkan นี่คือUnidecodeสำหรับ Java
Jakub Jirutka

4

ไม่มีวิธีง่ายๆหรือทั่วไปในการทำสิ่งที่คุณต้องการเพราะมันเป็นเพียงความคิดเห็นส่วนตัวของคุณที่ว่าตัวอักษรเหล่านี้ดูไม่เหมือนตัวอักษรละตินที่คุณต้องการแปลงเป็น จริงๆแล้วพวกเขาเป็นตัวอักษรที่แยกจากกันโดยมีชื่อและเสียงที่แตกต่างกันซึ่งดูเหมือนเผินๆจะดูเหมือนอักษรละติน

หากคุณต้องการแปลงนั้นคุณต้องสร้างตารางการแปลของคุณเองตามตัวอักษรละตินที่คุณคิดว่าควรแปลงเป็นตัวอักษรที่ไม่ใช่ภาษาละติน

(หากคุณต้องการลบเครื่องหมายกำกับเสียงเท่านั้นมีคำตอบบางส่วนในชุดข้อความนี้: ฉันจะลบตัวกำกับเสียง (สำเนียง) ออกจากสตริงใน. NET ได้อย่างไรอย่างไรก็ตามคุณอธิบายถึงปัญหาทั่วไปมากกว่า)


+1 นี่คือคำถาม "ลบการกำกับเสียง" เวอร์ชัน Java: stackoverflow.com/questions/1016955/… ; ดูคำตอบของ Michael Borgwardt และ devio
Jonik

4

ฉันไปงานปาร์ตี้สาย แต่หลังจากประสบปัญหาในวันนี้ฉันพบว่าคำตอบนี้ดีมาก:

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

อ้างอิง: https://stackoverflow.com/a/16283863


คำเตือนเล็ก ๆ - มันลบ U + 00DF LATIN SMALL LETTER SHARP S "ß"
rafalmag

และยังÆ ...
cactuschibre

4

ปัญหาในการ "แปลง" Unicode โดยพลการเป็น ASCII คือความหมายของอักขระขึ้นอยู่กับวัฒนธรรม ตัวอย่างเช่น "ß" เป็นคนที่พูดภาษาเยอรมันควรเปลี่ยนเป็น "ss" ในขณะที่ผู้พูดภาษาอังกฤษอาจแปลงเป็น "B"

เพิ่มความจริงที่ว่า Unicode มีจุดรหัสหลายจุดสำหรับร่ายมนตร์เดียวกัน

ผลที่สุดคือวิธีเดียวที่จะทำได้คือสร้างตารางขนาดใหญ่ที่มีอักขระ Unicode แต่ละตัวและอักขระ ASCII ที่คุณต้องการแปลงเป็น คุณสามารถใช้ทางลัดโดยการทำให้อักขระปกติโดยเน้นเสียงเป็นรูปแบบมาตรฐาน KD แต่อักขระบางตัวอาจไม่ปรับให้เป็น ASCII นอกจากนี้ Unicode ไม่ได้กำหนดว่าส่วนใดของสัญลักษณ์คือ "สำเนียง"

นี่คือข้อความที่ตัดตอนมาเล็กน้อยจากแอพที่ทำสิ่งนี้:

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...
}

ฉันเห็นด้วย. คุณควรสร้างพจนานุกรมของ Conversion สำหรับแอปพลิเคชันและผู้ชมที่คาดหวังโดยเฉพาะ ตัวอย่างเช่นสำหรับผู้ฟังที่พูดภาษาสเปนฉันจะแปลเฉพาะÁÉÍÓÚÜÑáéíóúü¿¡
Roberto Bonvallet

Roberto มีอักขระหลายพันตัวและฉันไม่สามารถทำคู่มือนี้ได้
AhmetB - Google

2
คุณใช้ภาษามนุษย์อะไรที่มีอักขระ "นับพัน" ญี่ปุ่น? คุณคาดหวังว่า converted うしようとしていますかจะถูกแปลงเป็นอะไร?
Dour High Arch

6
ตัวอย่างที่คุณให้มาไม่เหมาะ: U + 00DF LATIN SMALL LETTER SHARP S "ß" ไม่ใช่ตัวอักษร Unicode เดียวกับ U + 03B2 GREEK SMALL LETTER BETA "β"
Joachim Sauer

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.